-
-
Notifications
You must be signed in to change notification settings - Fork 69.5k
[Bug]: [msteams] startAccount resolves immediately causing restart loop and EADDRINUSE crash #24374
Description
Summary
Repo: https://github.com/openclaw/openclaw/issues/new
Title:[msteams] startAccount resolves immediately causing restart loop and EADDRINUSE crash
Body:
Summary
The @openclaw/msteams channel plugin crashes in a restart loop on every gateway start. Each attempt logs msteams server error and is retried with exponential backoff (up to 10 times), making the Teams connector permanently unavailable.
Environment
OpenClaw: 2026.2.22-2 (also present in 2026.2.19-2)
@openclaw/msteams: 2026.2.22
macOS 15.3 (arm64), Node 22.22.0
Microsoft Teams Bot Framework webhook on port 3978
Symptoms
Gateway log repeats indefinitely:
INFO starting provider (port 3978)
INFO msteams provider started on port 3978
INFO [default] auto-restart attempt 1/10 in 5s
INFO [default] auto-restart attempt 2/10 in 11s
ERROR msteams server error ← EADDRINUSE on second+ attempt
INFO [default] auto-restart attempt 3/10 in 22s
...
Root Cause
monitorMSTeamsProvider() in src/monitor.ts calls expressApp.listen(port, callback) and then immediately return { app: expressApp, shutdown } — it does not wait for the server lifecycle to end.
The channel orchestrator's contract (from plugin-sdk type definitions) is:
startAccount?: (ctx) => Promise
The Promise is expected to stay pending while the account is running, and only settle when it stops. When startAccount resolves immediately, the orchestrator treats the account as "exited" and schedules a restart. The new restart tries to bind port 3978 while the old Express server is still draining → EADDRINUSE → msteams server error.
Fix
Wrap the server in a long-lived Promise in src/monitor.ts:
return new Promise((resolve, reject) => {
const httpServer = expressApp.listen(port, () => {
log.info(msteams provider started on port ${port});
});
const shutdown = async () => {
log.info("shutting down msteams provider");
return new Promise((res) => {
httpServer.close((err) => {
if (err) log.debug?.("msteams server close error", { error: String(err) });
res();
});
});
};
httpServer.on("error", (err) => {
log.error("msteams server error", { error: String(err) });
reject(err);
});
httpServer.on("close", () => {
resolve({ app: expressApp, shutdown });
});
if (opts.abortSignal) {
opts.abortSignal.addEventListener("abort", () => {
void shutdown();
});
}
});
This fix has been applied locally and confirmed stable — no more restart loops, Teams connector holds port 3978 cleanly.
Additional Notes
Incoming messages still work even during the crash loop (the webhook receives fine); only auto-routing of replies is broken
The Cannot perform 'set/get' on a proxy that has been revoked errors seen separately are a consequence of this same issue — reply context expires before the retried server stabilizes
Steps to reproduce
- Configure a valid msteams channel in openclaw.json (appId, appPassword, tenantId)
- Start the OpenClaw gateway (
openclaw gateway) - Observe the gateway logs
Expected behavior
Expected: msteams provider starts once and stays running on port 3978
Actual behavior
Actual: Provider starts, then immediately restarts in a loop:
- "msteams provider started on port 3978"
- "[default] auto-restart attempt 1/10 in 5s"
- "[default] auto-restart attempt 2/10 in 11s"
- "msteams server error" (EADDRINUSE on second+ attempt)
- Repeats until giving up after 10 attempts
OpenClaw version
2026.2.22-2
Operating system
Macos 26.3
Install method
npm install -g openclaw
Logs, screenshots, and evidence
Impact and severity
No response
Additional information
No response