Hi, first of all thank you for this great project!
I ran into a case where the bot process looks healthy but never actually polls Telegram, so it stays silent until manually restarted. Sharing details below in case it helps.
Environment
- Package:
@grinev/opencode-telegram-bot v0.25.0 (installed via npm)
- Node.js v24.18.0 on linux x64
- Running as systemd system service (
opencode-telegram-bot.service, Type=simple, Restart=on-failure, ExecStart=/usr/local/bin/node .../opencode-telegram start)
- OpenCode server on port 4096 (user unit, healthy)
Symptoms
systemctl status opencode-telegram-bot showed active (running), PID alive, memory/CPU normal.
- Bot did not respond to any Telegram messages (
/sessions, /projects, /start stayed as pending updates, pending_update_count grew to 6).
- Manual
getUpdates against the same token succeeded with normal updates (no 409 Conflict), proving no active long-poller was running despite the alive process.
- After
systemctl restart, the bot logged Bot @OChelioBOT started! and manual getUpdates correctly returned 409 Conflict (single healthy poller). Token and network to api.telegram.org were fine the whole time.
Logs (startup that got stuck, IDs redacted)
[INFO] Starting OpenCode Telegram Bot v0.25.0...
[INFO] Allowed User ID: [REDACTED]
[INFO] [OpenCodeAutoRestart] Enabled: port=4096, intervalSec=300
[INFO] [OpenCodeReady] OpenCode server is ready: reason=auto_restart_startup
[INFO] [SessionCache] Pruned 3 stale directories from cache
[INFO] [PinnedManager] Restoring existing pinned message for session: ses_[REDACTED]
[INFO] [PinnedManager] Loaded 41 file diffs from messages
[INFO] [PinnedManager] Loaded context from history: 392732 tokens, cost: $2.35
[INFO] [Bot] Subscribing to OpenCode events for project: /home/.../codewalk
[INFO] [Attach] Attached to session: session=ses_[REDACTED], directory=/home/.../codewalk
[INFO] [Bot] Restored followed session after OpenCode ready: reason=auto_restart_startup
CLI error: Network request for 'getWebhookInfo' failed!
Note what is missing compared to a healthy startup: no [Bot] Dropping ~N update(s) queued while the bot was offline and no Bot @OChelioBOT started!. On healthy boots those two lines appear right after PinnedManager Restoring.... Earlier boots on the same machine also showed transient Could not clear global commands: HttpError: Network request for 'setMyCommands' failed! right before the same stuck state, so it looks like a transient Telegram API hiccup at startup is enough to trigger it.
Suspected cause (from installed dist/, may have shifted in source)
dist/app/bootstrap/start-bot-app.js:126: const webhookInfo = await bot.api.getWebhookInfo(); runs before bot.start() with no surrounding try/catch. If it throws, startBotApp() rejects.
dist/cli.js:192-203: the top-level runCli(...).catch(...) only writes CLI error: ... to stderr and sets process.exitCode = 1. It does not force exit.
- Meanwhile background work (
opencodeAutoRestartService.start(), scheduledTaskRuntime, heartbeat setInterval in dist/bot/index.js:85, PinnedManager restore via onReady) keeps the event loop alive, so the process never exits.
- Net effect:
bot.start({ drop_pending_updates: true, ... }) at start-bot-app.js:138 is never reached, no polling starts, but systemd still sees active (running) and Restart=on-failure never fires. A zombie that looks healthy.
I did not dig into the TypeScript sources, so paths above are from dist/ — happy to re-check against source if you point me at the right files.
Expected vs actual
- Expected: a Telegram API failure during startup either retries and then starts polling, or fails fast with process exit so the supervisor restarts it.
- Actual: process stays alive with exit code pending but no polling, requiring manual
systemctl restart.
Possible fixes (just ideas)
- Wrap the pre-
bot.start() Telegram calls (getWebhookInfo, deleteWebhook if needed) with retry + fail-fast process.exit(1) (after flushing logs/settings and stopping background services/timers) so Restart=on-failure works.
- Or ensure startup failure always tears down
heartbeatTimer, opencodeAutoRestartService, scheduledTaskRuntime, and listeners before rethrowing, so Node can actually exit when exitCode is set.
- Optionally log a clear
FATAL: Telegram polling never started line to make the zombie state obvious.
Workaround for others hitting this
sudo systemctl restart opencode-telegram-bot, then confirm Bot @... started! in journalctl -u opencode-telegram-bot and that a manual getUpdates returns 409 Conflict (healthy single poller).
Happy to provide more logs or test a fix. Thanks again for maintaining this!
Hi, first of all thank you for this great project!
I ran into a case where the bot process looks healthy but never actually polls Telegram, so it stays silent until manually restarted. Sharing details below in case it helps.
Environment
@grinev/opencode-telegram-botv0.25.0 (installed via npm)opencode-telegram-bot.service,Type=simple,Restart=on-failure,ExecStart=/usr/local/bin/node .../opencode-telegram start)Symptoms
systemctl status opencode-telegram-botshowedactive (running), PID alive, memory/CPU normal./sessions,/projects,/startstayed as pending updates,pending_update_countgrew to 6).getUpdatesagainst the same token succeeded with normal updates (no409 Conflict), proving no active long-poller was running despite the alive process.systemctl restart, the bot loggedBot @OChelioBOT started!and manualgetUpdatescorrectly returned409 Conflict(single healthy poller). Token and network toapi.telegram.orgwere fine the whole time.Logs (startup that got stuck, IDs redacted)
Note what is missing compared to a healthy startup: no
[Bot] Dropping ~N update(s) queued while the bot was offlineand noBot @OChelioBOT started!. On healthy boots those two lines appear right afterPinnedManager Restoring.... Earlier boots on the same machine also showed transientCould not clear global commands: HttpError: Network request for 'setMyCommands' failed!right before the same stuck state, so it looks like a transient Telegram API hiccup at startup is enough to trigger it.Suspected cause (from installed
dist/, may have shifted in source)dist/app/bootstrap/start-bot-app.js:126:const webhookInfo = await bot.api.getWebhookInfo();runs beforebot.start()with no surrounding try/catch. If it throws,startBotApp()rejects.dist/cli.js:192-203: the top-levelrunCli(...).catch(...)only writesCLI error: ...to stderr and setsprocess.exitCode = 1. It does not force exit.opencodeAutoRestartService.start(),scheduledTaskRuntime, heartbeatsetIntervalindist/bot/index.js:85, PinnedManager restore viaonReady) keeps the event loop alive, so the process never exits.bot.start({ drop_pending_updates: true, ... })atstart-bot-app.js:138is never reached, no polling starts, but systemd still seesactive (running)andRestart=on-failurenever fires. A zombie that looks healthy.I did not dig into the TypeScript sources, so paths above are from
dist/— happy to re-check against source if you point me at the right files.Expected vs actual
systemctl restart.Possible fixes (just ideas)
bot.start()Telegram calls (getWebhookInfo,deleteWebhookif needed) with retry + fail-fastprocess.exit(1)(after flushing logs/settings and stopping background services/timers) soRestart=on-failureworks.heartbeatTimer,opencodeAutoRestartService,scheduledTaskRuntime, and listeners before rethrowing, so Node can actually exit whenexitCodeis set.FATAL: Telegram polling never startedline to make the zombie state obvious.Workaround for others hitting this
sudo systemctl restart opencode-telegram-bot, then confirmBot @... started!injournalctl -u opencode-telegram-botand that a manualgetUpdatesreturns409 Conflict(healthy single poller).Happy to provide more logs or test a fix. Thanks again for maintaining this!