Skip to content

fix(core): guard print() calls in run_conversation() against OSError#1668

Merged
teknium1 merged 1 commit intomainfrom
hermes/hermes-923bc090
Mar 17, 2026
Merged

fix(core): guard print() calls in run_conversation() against OSError#1668
teknium1 merged 1 commit intomainfrom
hermes/hermes-923bc090

Conversation

@teknium1
Copy link
Copy Markdown
Contributor

Summary

Reimplementation of the fix from PR #1358 by @mr-emmett-one (323 commits stale, had a variable deletion bug). Addresses issue #845.

Problem

In headless environments (systemd services, Docker, nohup), stdout can become unavailable mid-session. Raw print() raises OSError: [Errno 5] Input/output error. The critical failure cascade:

  1. print() in quiet_mode display raises OSError
  2. The except Exception handler catches it
  3. The error handler's own print(f"❌ {error_msg}") also raises OSError
  4. This propagates out of run_conversation() entirely
  5. Cron scheduler marks the job as error — completed work is never delivered

Fix

  • _safe_print() — new static method that wraps print() with try/except OSError. Silently drops output when stdout is broken.
  • _vprint() now uses _safe_print() — protects all calls through the verbose print path automatically.
  • Raw print() calls in run_conversation() hot path converted to _safe_print(): starting conversation, interrupt, budget exhausted, preflight compression, context cache, conversation completed.
  • Error handler (the cascading crash point) gets explicit try/except with logger.error() fallback so diagnostic messages aren't silently lost.

Fixes #845

In headless environments (systemd, Docker, nohup) stdout can become
unavailable mid-session. Raw print() raises OSError which crashes
cron jobs — agent finishes work but delivery never happens because
the error handler's own print() also raises OSError.

Fix:
- Add _safe_print() static method that wraps print() with try/except
  OSError — silently drops output when stdout is broken
- Make _vprint() use _safe_print() — protects all calls through the
  verbose print path
- Convert raw print() calls in run_conversation() hot path to use
  _safe_print(): starting conversation, interrupt, budget exhausted,
  preflight compression, context cache, conversation completed
- Error handler print (the cascading crash point) gets explicit
  try/except with logger.error() fallback so diagnostics aren't lost

Fixes #845
Closes #1358 (superseded — PR was 323 commits stale with a bug)
@teknium1 teknium1 merged commit a3ac142 into main Mar 17, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant