Skip to content

fix: error context preservation, WAL checkpoint, hook timeout - #6622

Open
aaronlab wants to merge 2 commits into
NousResearch:mainfrom
aaronlab:fix/error-context-wal-checkpoint-hook-timeout
Open

fix: error context preservation, WAL checkpoint, hook timeout#6622
aaronlab wants to merge 2 commits into
NousResearch:mainfrom
aaronlab:fix/error-context-wal-checkpoint-hook-timeout

Conversation

@aaronlab

@aaronlab aaronlab commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR addresses three reliability gaps found during iteration #4 of a deep code audit:

  • Preserve original error context in call_llm retry chain (agent/auxiliary_client.py): When the max_tokens retry also fails with a payment error, the original error was silently overwritten via first_err = retry_err (line 2089), losing the initial diagnostic context. Now uses retry_err.__cause__ = first_err for proper Python exception chaining, preserving the full error sequence for debugging.

  • Guard payment fallback provider call (agent/auxiliary_client.py): The fallback API call (line 2105) had no try/except. If the fallback provider also failed, there was zero logging and no indication that a fallback was even attempted. Added error handling with a warning log message identifying which fallback provider failed.

  • Add WAL checkpoint to holographic memory store (plugins/memory/holographic/store.py): WAL mode was enabled (line 130) but no checkpoint mechanism existed anywhere in the class, unlike hermes_state.py which has proper _try_wal_checkpoint(). This causes unbounded WAL file growth over long-running sessions. Added periodic PASSIVE checkpoint every 50 writes and a final checkpoint on close(), following the established pattern.

  • Add timeout protection to plugin hook invocation (hermes_cli/plugins.py): Hook callbacks had good exception isolation (try/except per callback) but no timeout protection. A misbehaving plugin could block the agent loop indefinitely with a blocking call. Added a 30-second timeout using ThreadPoolExecutor, with warning logging on timeout.

Files Changed

File Change
agent/auxiliary_client.py Exception chaining for error context + fallback error handling
plugins/memory/holographic/store.py WAL checkpoint method + periodic + on-close checkpoint
hermes_cli/plugins.py 30s timeout for hook callbacks via ThreadPoolExecutor

Test plan

  • Simulate primary provider max_tokens error → payment retry → verify original error in __cause__
  • Simulate fallback provider failure → verify warning log is emitted
  • Insert 100+ facts into holographic store → verify WAL file stays bounded
  • Create a plugin with time.sleep(60) in pre_llm_call hook → verify 30s timeout warning
  • Run existing test suite: pytest tests/

🤖 Generated with Claude Code

aaronlab and others added 2 commits April 9, 2026 20:54
…agent loop reliability

## Summary
Found 5 critical bugs in async error handling, context compression, and cron scheduling:

**CRITICAL (2):**
1. Role violation after context compression (context_compressor.py:694-728)
   - Tool message validation missing when merging summary
   - Causes API crash and data loss after compression

2. Double-execution race condition in cron scheduler (scheduler.py:843-892)
   - File lock released before job execution completes
   - Allows duplicate jobs to be executed (DoS, duplicate messages)

**HIGH (1):**
3. Unhandled context compression exceptions in main loop (run_agent.py:8204,8262,8338)
   - Silent crash when summarizer fails during API loop
   - No graceful degradation

**MEDIUM (2):**
4. Error swallowing in auxiliary_client (auxiliary_client.py:2074-2106)
   - Original error overwritten on retry failure
   - Lost error context, unreachable fallback logic

5. Session ID change without exception recovery (run_agent.py:6041-6071)
   - Session state corruption on DB failures
   - Broken session lineage

## Details
Full analysis with code snippets, scenarios, and fixes in:
- AUDIT_ITERATION_2.md (400 lines, detailed technical analysis)
- AUDIT_ITERATION_2_SUMMARY.txt (visual summary, testing recommendations)

## Recommended Priority
1. Bug NousResearch#1 (Role Violation) - FIX IMMEDIATELY
2. Bug NousResearch#2 (Double Execution) - FIX IMMEDIATELY
3. Bug NousResearch#3 (Unhandled Exceptions) - FIX SOON
4. Bug NousResearch#4 & NousResearch#5 - FIX AFTER critical bugs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tection

- Preserve original error context in call_llm retry chain (agent/auxiliary_client.py):
  When max_tokens retry fails with a payment error, the original error was silently
  overwritten via `first_err = retry_err`, losing diagnostic context. Now chains
  the original error via `__cause__` for proper Python exception chaining.

- Guard fallback provider call (agent/auxiliary_client.py):
  Payment fallback API call at line 2105 had no try/except. If the fallback
  provider also failed, there was no logging and no indication the fallback was
  attempted. Added error handling with warning log on fallback failure.

- Add WAL checkpoint to holographic memory store (plugins/memory/holographic/store.py):
  WAL mode was enabled but no checkpoint mechanism existed, causing unbounded WAL
  file growth over time. Added periodic checkpoint every 50 writes and a final
  checkpoint on close(), following the same pattern used in hermes_state.py.

- Add timeout protection to plugin hook invocation (hermes_cli/plugins.py):
  Plugin hook callbacks had exception isolation but no timeout protection. A
  misbehaving plugin could block the agent loop indefinitely. Added 30-second
  timeout using ThreadPoolExecutor with proper warning logging on timeout.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins labels Apr 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #16510 (WAL checkpoint pattern) and #6684 (write_count race in hermes_state.py). WAL checkpoint portion overlaps with existing work.

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #16510 (WAL checkpoint pattern) and #6684 (write_count race in hermes_state.py). WAL checkpoint portion overlaps with existing work.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the reliability audit and for identifying the hook and holographic-store failure modes. The current patch needs substantial rework before it can be safely salvaged.

Problems

  • The hook timeout does not actually bound execution: with ThreadPoolExecutor(...) waits during executor shutdown after future.result(timeout=...) times out, so the blocked callback still blocks the caller. The current dispatch remains synchronous at hermes_cli/plugins.py:1913-1925.
  • wal_checkpoint(PASSIVE) does not shrink the WAL high-water mark. Main commit 46b2afc56b79 changed SessionDB from PASSIVE to TRUNCATE specifically for this reason. The proposed counter also covers only add_fact, while update_fact, remove_fact, and record_feedback commit independently at plugins/memory/holographic/store.py:286-392.
  • agent/auxiliary_client.py has been extensively refactored since this branch. Current sync and async max-token retry paths are at :6651-6656 and :7202-7207, and fallback calls now use _call_fallback_candidate_sync at :6930-6952; the direct fallback-call hunk no longer applies.

Suggested changes

  • Rework the timeout so its timeout path cannot wait for the callback worker, and add a blocked-hook regression test.
  • Port the WAL work to the current SessionDB TRUNCATE pattern and cover every public write path.
  • Rework and test exception chaining across both sync and async auxiliary fallback paths.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 12, 2026

@GottZ GottZ left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was generated by AI during triage.

Summary

Four open PRs reference this audit-derived reliability complex, but they address distinct causes: #6622 covers error chaining, fallback logging, hook timeouts, and WAL checkpointing; #6627 covers concurrent-tool timeouts, file handling, and RPC decoding; #6629 covers token estimation, context-length diagnostics, and batch durability; #6635 covers Anthropic tool-prefix handling and parse/shutdown observability. Each diff contains superseded, ineffective, incorrect, or incomplete changes, so none currently provides a mergeable consolidation target.

Related pull requests

  • #6622 related — (+670/-4) — needs substantial rework: The error-context and fallback diagnostics target code that has since been refactored, the ThreadPoolExecutor context still waits for a timed-out hook, and PASSIVE checkpointing neither truncates the WAL nor covers all write paths. The contributor keep_open review supports salvaging the hook and holographic-store concerns only after rebasing, using genuinely non-blocking timeout dispatch, applying an appropriate checkpoint policy across every committed mutation, and adding tests.
  • #6627 related — (+639/-4) — narrow salvage only: The concurrent-tool timeout and cwd handle fixes are already present on main, while replacement decoding can silently corrupt structured JSON and still fails for malformed syntax. Consistent with the keep_open review, retain strict UTF-8 and salvage only explicit RPC protocol-error handling with a malformed-response regression test.
  • #6629 related — (+647/-3) — needs focused rework: The token-estimation correction has already landed, but the fallback diagnostic and fsync durability concerns remain relevant; the proposed Pool.join(timeout=10) calls are invalid and the configuration guidance is inaccurate. Consistent with the keep_open review, remove the landed and unrelated material, use valid worker cleanup, and test fallback logging, durable trajectory writes, and interruption paths before merge.
  • #6635 related — (+635/-1) — needs focused rework: OAuth prefix stripping already exists on main, the gateway hunk inspects a write-only map rather than the adapters' actual queued follow-ups, and malformed argument logging covers only one of two fallback paths. Consistent with the keep_open review, remove the landed prefix hunk, instrument both parse fallbacks and the real pending-message stores, and add focused regression tests.

Duplicates

#6622, #6627, #6629, and #6635 duplicate the same unrelated AUDIT_ITERATION_2.md and AUDIT_ITERATION_2_SUMMARY.txt additions; their substantive code changes are not duplicates and should not be consolidated into one PR.

Suggested consolidation

Merge none as-is. Keep #6622, #6627, #6629, and #6635 open only for the focused salvage described in their contributor keep_open reviews, remove the duplicated audit documents and already-landed hunks, and split the remaining unrelated fixes into independently tested PRs; no substantive PR can currently be closed as a duplicate of another.

Cross-PR triage: Reviewed 4 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 110 kB of PR diffs, 8 kB of issue/PR text, 6 kB of discussion (6 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants