Problem
When a scheduled job fires while the bot is actively processing a user message (which can take several minutes via claude.run_command()), the event bus blocks and the scheduled job either gets delayed significantly or missed entirely.
I observed heartbeat jobs being missed by 8+ minutes because AgentHandler.handle_scheduled awaits claude.run_command() synchronously, which blocks EventBus._process_events from processing any subsequent events until the Claude execution finishes.
APScheduler logs: "Run time of job Heartbeat was missed by 0:08:54"
Root Cause
AgentHandler.handle_scheduled (in src/events/handlers.py) calls await self.claude.run_command(...) directly. Since the event bus dispatches events sequentially via _dispatch, a long-running Claude execution blocks the entire bus.
Proposed Fix
Change handle_scheduled to dispatch work via asyncio.create_task() and return immediately. This keeps the event bus sequential (safe) while scheduled jobs run in the background.
Key elements:
asyncio.create_task() + return immediately from handle_scheduled
asyncio.Semaphore(2) to cap concurrent Claude executions
task.add_done_callback() for cleanup and error logging
handle_webhook stays unchanged (webhooks are fast, blocking is fine)
I have a working implementation with tests — happy to open a PR if this approach looks right.
Environment
- claude-code-telegram v1.6.0
- APScheduler 3.11.x
- Multiple scheduled jobs (heartbeats every 4h, morning briefing, weekly digest)
Problem
When a scheduled job fires while the bot is actively processing a user message (which can take several minutes via
claude.run_command()), the event bus blocks and the scheduled job either gets delayed significantly or missed entirely.I observed heartbeat jobs being missed by 8+ minutes because
AgentHandler.handle_scheduledawaitsclaude.run_command()synchronously, which blocksEventBus._process_eventsfrom processing any subsequent events until the Claude execution finishes.APScheduler logs:
"Run time of job Heartbeat was missed by 0:08:54"Root Cause
AgentHandler.handle_scheduled(insrc/events/handlers.py) callsawait self.claude.run_command(...)directly. Since the event bus dispatches events sequentially via_dispatch, a long-running Claude execution blocks the entire bus.Proposed Fix
Change
handle_scheduledto dispatch work viaasyncio.create_task()and return immediately. This keeps the event bus sequential (safe) while scheduled jobs run in the background.Key elements:
asyncio.create_task()+ return immediately fromhandle_scheduledasyncio.Semaphore(2)to cap concurrent Claude executionstask.add_done_callback()for cleanup and error logginghandle_webhookstays unchanged (webhooks are fast, blocking is fine)I have a working implementation with tests — happy to open a PR if this approach looks right.
Environment