#17918: fix(telegram): start webhook listener before setWebhook + forward webhookPort from config
channel: telegram
size: S
experienced-contributor
Cluster:
Webhook Configuration and Resilience
Fixes #12902
## Problem
The Telegram webhook listener calls `setWebhook` (registering the URL with Telegram) **before** the HTTP server is actually listening. This race means Telegram immediately sends a verification request to a port that isn't open yet, resulting in a 502 Bad Gateway. Additionally, the `webhookPort` config field was accepted in the schema but never forwarded to the listener.
## Fix
**Reversed startup order** — the HTTP server now binds and starts listening *before* calling `setWebhook`, eliminating the race condition.
**Cleanup on failure** — if `setWebhook` fails after the listener is up, the server is properly closed, the bot stopped, and diagnostics halted. Previously a failed `setWebhook` left an orphaned HTTP server.
**Resolved port** — uses `server.address().port` for the public URL instead of the configured port, which is critical when port `0` is used (dynamic allocation) and ensures the URL always matches the actual bound port.
**webhookPort config forwarding** — the `webhookPort` field from account config is now properly passed through the channel plugin → monitor → webhook pipeline.
## Changes
- `src/telegram/webhook.ts` — reversed listen/setWebhook order, added failure cleanup, use resolved port
- `src/telegram/monitor.ts` — fall back to `account.config.webhookPort` when no CLI override
- `extensions/telegram/src/channel.ts` — forward `webhookPort` from config
- `src/config/types.telegram.ts` — add `webhookPort` to type definition
- `src/config/zod-schema.providers-core.ts` — add `webhookPort` to zod schema
- `src/telegram/webhook.test.ts` — new test for cleanup on setWebhook failure, port resolution assertions
- `src/telegram/monitor.test.ts` — updated test to verify webhookPort passthrough
- `pnpm-lock.yaml` — removed unused `proper-lockfile` dependency
## Tests
All existing and new tests pass:
- Verifies server binds before `setWebhook` is called
- Verifies resolved port appears in webhook URL (no `:0/` in URL)
- Verifies server closes cleanly when `setWebhook` fails
- Verifies `webhookPort` config is forwarded from monitor to webhook
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes a race condition in the Telegram webhook startup where `setWebhook` was called before the HTTP server was actually listening, causing Telegram's verification request to hit an unopened port (502). The fix reverses the startup order so the server binds first, then registers the webhook URL with Telegram. Additionally, `webhookPort` from account config is now properly forwarded through the channel plugin and monitor layers.
- **Race condition fix**: Server now listens before `setWebhook` is called, ensuring the port is ready for Telegram's verification request
- **Port resolution**: Uses `server.address().port` for the public URL, correctly handling dynamic port allocation (port `0`)
- **Cleanup on failure**: If `setWebhook` fails after the listener starts, the server is closed, bot stopped, and diagnostics halted — previously this left an orphaned HTTP server
- **Config forwarding**: `webhookPort` is now passed from account config through the channel plugin → monitor → webhook pipeline, matching the existing pattern for `webhookHost` and `webhookSecret`
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge — it fixes a clear race condition with a well-structured solution and includes appropriate tests.
- All changes are focused and correct. The listen-before-register pattern eliminates the race condition. Error cleanup properly tears down the server and bot. The port resolution handles both static and dynamic (port 0) allocation. The webhookPort config forwarding follows existing patterns exactly. Tests cover port resolution and config passthrough. No security concerns, no breaking changes to the public API.
- No files require special attention.
<sub>Last reviewed commit: ab7c8b2</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20420: Telegram webhook listener to use the gateway's HTTP server instead ...
by kesor · 2026-02-18
81.5%
#22694: telegram: stabilize multi-account webhook mode
by Dongik · 2026-02-21
81.0%
#7754: fix(telegram): configurable webhook timeout
by djmango · 2026-02-03
77.6%
#11804: fix(webhook): return 503 from health endpoints when last processing...
by coygeek · 2026-02-08
76.3%
#20395: fix(googlechat): prevent infinite auto-restart and ambiguous-target...
by ggalmeida0 · 2026-02-18
75.8%
#10865: telegram: fast-ACK webhook and retry bind on EADDRINUSE
by u9733037 · 2026-02-07
75.3%
#18115: fix: prevent voice message loss during concurrent update processing
by AlekseyRodkin · 2026-02-16
75.3%
#13521: telegram: require webhook secret in runtime webhook mode
by davidahmann · 2026-02-10
75.1%
#8067: fix(telegram): use timing-safe comparison for webhook secret
by yubrew · 2026-02-03
75.0%
#23134: fix(gateway): skip auto-restart for webhook channels that resolve i...
by puneet1409 · 2026-02-22
75.0%