Skip to content

fix(cron): use non-blocking delivery for bot-chat to avoid session lock conflicts - #102659

Closed
itsflownium wants to merge 1 commit into
NousResearch:mainfrom
itsflownium:fix/issue-99956-bot-chat-delivery-lock
Closed

itsflownium wants to merge 1 commit into
NousResearch:mainfrom
itsflownium:fix/issue-99956-bot-chat-delivery-lock

Conversation

@itsflownium

@itsflownium itsflownium commented Sep 4, 2026 •

Copy link
Copy Markdown

Fixes #99956

Problem

When a scheduled cron job uses deliver: bot-chat (or bot-chat:<profile>), _deliver_to_bot_chat in cron/scheduler.py spawns hermes chat --in ~ -c "Bot Chat" ... as a synchronous subprocess. Hermes enforces a single-owner session lease — only one surface (CLI/TUI/Desktop) may run a session at a time. If the target profile already has an active interactive session, the subprocess exits with code 1 and the delivery is permanently dropped:

Session <id> already has a live owner (cli, pid <pid>, running <time>). Only one surface at a time may run a session...

Root Cause

_deliver_to_bot_chat fired the hermes chat subprocess with no fallback. Unlike tools/bot_mode_dm.py (which coordinates delivery through per-profile turn locks), the cron path had no lock coordination and no retry — a transient in-flight turn or a concurrently open surface caused a hard drop.

Fix

In cron/scheduler.py:

  • Run the delivery subprocess under the per-profile relay turn lock (_bot_chat_delivery_lock, mirroring tools.bot_mode_dm._delivery_lock), so the scheduled subprocess is not an uncoordinated writer. Degrades to a no-op if the relay lock machinery is unavailable.
  • Detect single-owner session-lease refusals (_is_session_lock_refusal: "already has a live owner" / "active session limit") and retry them with exponential backoff — delays double 2s → 4s → 8s → 16s, ~30s total — before giving up.
  • Emit distinct actionable "skipped" messages for the two refusal classes (another surface owns the session vs. machine at the active-session cap) instead of a raw exit-code error. Non-lock failures (auth/quota/config/model) are not retried, since a retry cannot fix them.

Verification

  • tests/cron/test_cron_bot_chat_delivery.py covers token parsing, target resolution, the delivery lane, and the fix(cron): bot-chat delivery fails when target profile has active session lock #99956 paths: retry-then-succeed, give-up-after-retries, active-session-limit message, non-lock failures not retried, exponential backoff within 30s, and turn-lock key resolution.
  • uv run pytest tests/cron/test_cron_bot_chat_delivery.py → 26 passed.

@itsflownium
itsflownium force-pushed the fix/issue-99956-bot-chat-delivery-lock branch from a576fff to 04c07f7 Compare September 4, 2026 04:24
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management area/sessions Session lifecycle, resume, persistence, history sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Sep 4, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Related: competing fixes for #99956 — #100319 (durable delivery queue drained once the live owner releases) and #70406 (exact-session inject). Note the diff here is a bounded blocking retry (5 attempts, 2s→16s backoff) rather than a non-blocking path, so it only clears transient contention; a Bot Chat held open indefinitely on another surface still fails after the retries. Maintainer should pick one mechanism.

@crazyief crazyief left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lab: tests/cron/test_cron_bot_chat_delivery.py → 26 passed. Bounded retry on session-lock refusal plus per-profile turn lock is the right bot-chat delivery fix. Not an approval.

@teknium1

teknium1 commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks @itsflownium for separating lease/cap contention from other delivery failures and coordinating cron with relay turns. #105442 is merged at 5280fe99872a58f1b9d25b00a79d240ecd035f7d. Cron now hands output directly to an advertised Desktop/TUI owner, so bounded CLI retries are unnecessary for that permanent-owner scenario.

Leaving this PR open because its legacy-lane turn-lock coordination and bounded recovery from machine active-session-cap or unsupported-owner refusals are not covered by that change. Main still makes a single subprocess attempt in the unowned/unsupported-consumer fallback. The remaining scope is contention handling on that fallback, not non-blocking delivery to a permanently open Desktop chat.

@teknium1

Copy link
Copy Markdown
Collaborator

Thanks @itsflownium — the session-lock conflict this PR worked around is resolved on main by a different mechanism: 5280fe9 routes a cron bot-chat delivery into a Desktop/TUI-held Bot Chat through the durable live-owner ingress, and 5d8390d defers the payload for a CLI-only owner instead of racing the lease (cron/scheduler_delivery.py::_deliver_to_bot_chat). The threaded-subprocess approach here is no longer needed. Closing as superseded, with credit.

@teknium1 teknium1 closed this Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cron Cron scheduler and job management P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(cron): bot-chat delivery fails when target profile has active session lock

4 participants