#20309: [BUG]: fix telegram webhook should wait for abort signal instead of returning
channel: telegram
size: XS
Cluster:
Webhook Configuration and Resilience
## Summary
Describe the problem and fix in 2–5 bullets:
- Problem: The `monitorTelegramProvider` function was returning immediately after starting the webhook server, causing the gateway to interpret this as a channel exit and trigger automatic restarts, leading to `EADDRINUSE` errors.
- Why it matters: Webhook mode is unusable when the gateway continuously crashes trying to rebind the same port.
- What changed: Added a Promise in the monitor that waits for the abort signal after starting the webhook, keeping the monitor function alive until shutdown. The webhook startup function itself returns immediately with its handle, avoiding deadlocks.
- What did NOT change (scope boundary): Polling mode, bot creation, webhook registration with Telegram API, webhook startup function signature, or any other channel implementations.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [x] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## User-visible / Behavior Changes
Telegram webhook mode now works correctly without crashing. The gateway no longer attempts to restart the Telegram channel when using webhook mode, eliminating the `EADDRINUSE` port conflict errors.
## Security Impact (required)
- New permissions/capabilities? No
- Secrets/tokens handling changed? No
- New/changed network calls? No
- Command/tool execution surface changed? No
- Data access scope changed? No
## Repro + Verification
### Environment
- OS: Linux NixOS 25.11
- Runtime/container: systemd
- Model/provider: not relevant
- Integration/channel (if any): Telegram
- Relevant config (redacted):
```json
{
"channels": {
"telegram": {
"webhookUrl": "https://xxx.xxxx.xxx/telegram/webhook",
"webhookSecret": "xxxxx",
"webhookPath": "/telegram/webhook",
"webhookHost": "127.0.0.1"
}
}
}
```
### Steps
1. Configure Telegram webhook listener (see configuration above)
2. Start gateway
3. Observe logs showing webhook starting successfully
4. Gateway immediately logs "auto-restart attempt 1/10 in 5s"
5. Second start attempt fails with `EADDRINUSE: address already in use 127.0.0.1:8787`
### Expected
- Webhook server starts once and stays running until gateway shutdown
- No automatic restart attempts
- Telegram messages are received and processed via webhook
### Actual (before fix)
- Webhook server starts successfully
- Monitor function returns immediately
- Gateway interprets this as channel exit and schedules restart
- Second start attempt fails with port conflict
- Gateway crashes or enters restart loop
### Actual (after fix)
- Webhook server starts and monitor stays running
- No restart attempts
- Telegram webhook receives and processes messages correctly
## Evidence
Attach at least one:
- [x] Trace/log snippets
- [x] Test added
**Before fix:**
```
2026-02-18T14:09:00.683Z [telegram] [default] starting provider (@XxxXxxBot)
2026-02-18T14:09:01.076Z [telegram] webhook listening on https://xxx.xxxx.xxx/telegram/webhook
2026-02-18T14:09:01.077Z [telegram] [default] auto-restart attempt 1/10 in 5s
2026-02-18T14:09:07.019Z [telegram] [default] starting provider (@XxxXxxBot)
2026-02-18T16:09:07.396+02:00 [openclaw] Uncaught exception: Error: listen EADDRINUSE: address already in use 127.0.0.1:8787
```
**After fix:**
```
2026-02-18T16:15:00.123Z [telegram] [default] starting provider (@XxxXxxBot)
2026-02-18T16:15:00.456Z [telegram] webhook listening on https://xxx.xxxx.xxx/telegram/webhook
[no restart attempts, webhook stays running]
```
**Test added:**
Added test `"webhook mode waits for abort signal before returning"` in `monitor.test.ts` that verifies the monitor stays running until abort is triggered.
## Human Verification (required)
What you personally verified (not just CI), and how:
- Verified scenarios:
- Configured Telegram webhook mode with external reverse proxy
- Started gateway and confirmed webhook server starts and stays running
- Verified no automatic restart attempts in logs
- Confirmed Telegram messages are received via webhook
- Ran existing tests to confirm no regressions
- Edge cases checked:
- Gateway shutdown properly stops webhook server
- Abort signal correctly triggers cleanup
- Webhook startup function returns handle immediately (no deadlock)
- What you did **not** verify:
- Behavior with multiple Telegram accounts (only tested single account)
- Interaction with Telegram API rate limits during webhook registration
## Compatibility / Migration
- Backward compatible? Yes
- Config/env changes? No
- Migration needed? No
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: Switch to polling mode by removing `webhookUrl` from Telegram config, or revert the commit
- Files/config to restore: `src/telegram/monitor.ts` (primary change)
- Known bad symptoms reviewers should watch for: Monitor not stopping on shutdown, webhook server not responding to Telegram updates, memory leak from unclosed connections
## Risks and Mitigations
- Risk: If abort signal handling is broken, monitor might not stop cleanly on gateway shutdown
- Mitigation: Abort signal is checked before waiting; pattern matches other channels; test added to verify behavior
- Risk: Promise never resolves if abort signal is never fired
- Mitigation: Only waits when `abortSignal` is provided and not already aborted; gateway shutdown always fires abort signals
Most Similar PRs
#20420: Telegram webhook listener to use the gateway's HTTP server instead ...
by kesor · 2026-02-18
80.4%
#22694: telegram: stabilize multi-account webhook mode
by Dongik · 2026-02-21
79.0%
#22322: fix(googlechat): keep webhook monitor alive until abort
by AIflow-Labs · 2026-02-21
75.5%
#22367: fix(whatsapp): prevent permanent listener loss after abort during r...
by mcinteerj · 2026-02-21
74.8%
#17918: fix(telegram): start webhook listener before setWebhook + forward w...
by Glucksberg · 2026-02-16
73.8%
#21956: fix(line): block monitorLineProvider on abort signal to prevent cra...
by lailoo · 2026-02-20
73.1%
#20551: fix(googlechat): prevent health monitor restart loop and add JWT ve...
by FredCat32 · 2026-02-19
71.8%
#22605: fix(msteams): keep provider promise pending until abort to stop aut...
by OpakAlex · 2026-02-21
71.2%
#23621: fix(LINE): keep startAccount promise alive to prevent auto-restart ...
by ttakanawa · 2026-02-22
71.0%
#20455: fix(msteams): prevent EADDRINUSE restart loop
by taradtke · 2026-02-18
70.3%