hermes - ✅(Solved) Fix [Bug]: read_file dedup stub never triggers loop detection — infinite read loop [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#15759Fetched 2026-04-26 05:25:15
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×1referenced ×1

Error Message

read_file → content (dedup mtime cached) read_file → dedup stub (dedup_hits[key]=1) read_file → BLOCKED (dedup_hits[key]=2 → hard error: "STOP calling read_file")

Root Cause

The existing loop-detection guard at tools/file_tools.py:538 (hard block after 4 consecutive reads) and the warning at line 549 (after 3 consecutive reads) are never reached for dedup stubs because the dedup path returns early at line 447.

Fix Action

Fixed

PR fix notes

PR #15762: fix(tools): dedup early-return bypasses loop detection in read_file (#15759)

Description (problem / solution / changelog)

Summary

  • The read_file dedup path at tools/file_tools.py:435 returned immediately for unchanged-file hits without updating the consecutive re-read counter.
  • The hard-block guard (count >= 4) and the warning guard (count >= 3) at line 538 were unreachable for dedup hits.
  • Models that repeatedly call read_file on an unchanged file received an infinite stream of dedup stubs with no escalation.

The bug

_read_tracker[task_id]["consecutive"] is only incremented in the full-read path (line 504). The dedup path returned before that code ran:

read_file → content      (mtime cached, consecutive=1)
read_file → dedup stub   (returns early — consecutive stays at 1 forever)
read_file → dedup stub   ← infinite loop

Issue reported with Qwen3.6 local models that became stuck re-reading files mid-task after a hermes update.

The fix

Before returning the dedup stub, acquire _read_tracker_lock and update last_key / consecutive using the same logic as the full-read path. If count >= 4, return the BLOCKED error instead of the stub. If count >= 3, annotate the stub with _warning.

notify_other_tool_call() already zeroes consecutive, so interleaved tool calls continue to work correctly.

Test plan

  • Before: test_dedup_fourth_read_is_hard_blocked and test_dedup_third_read_returns_stub_with_warning fail (stub returned with no warning or block)
  • After: all 30 loop-detection tests pass including 5 new dedup-specific cases
  • Regression guard: reverted fix → observed dedup returning plain stub on 4th call; restored → 0 failures
  • CI baseline: 8 pre-existing failures (7 dingtalk + 1 matrix) on origin/main — unchanged

Related

  • Fixes #15759

🤖 Generated with Claude Code

Changed files

  • tests/tools/test_read_loop_detection.py (modified, +90/-0)
  • tools/file_tools.py (modified, +34/-3)

Code Example

{"content": "File unchanged since last read. The content from the earlier read_file result in this conversation is still current — refer to that instead of re-reading.", "path": "...", "dedup": true}

---

read_file → content      (dedup mtime cached)
read_file → dedup stub   (dedup_hits[key]=1)
read_file → BLOCKED      (dedup_hits[key]=2 → hard error: "STOP calling read_file")

---

read_file → content      (consecutive=1)
read_file → dedup stub   (dedup path returns early, never hits loop detection)
read_file → dedup stub   (same stub forever)
...

---

2026-04-26 04:44:26,890 INFO hermes_cli.plugins: Plugin 'openai' registered image_gen provider: openai
2026-04-26 04:44:26,891 INFO hermes_cli.plugins: Plugin 'openai-codex' registered image_gen provider: openai-codex
2026-04-26 04:44:26,921 INFO hermes_cli.plugins: Plugin 'xai' registered image_gen provider: xai
2026-04-26 04:44:26,959 INFO hermes_cli.plugins: Plugin 'hermes-lcm' registered context engine: lcm
2026-04-26 04:44:26,959 INFO hermes_plugins.hermes_lcm: LCM slash command registration disabled (set LCM_ENABLE_SLASH_COMMAND=1 to enable /lcm)
2026-04-26 04:44:26,959 INFO hermes_plugins.hermes_lcm: LCM plugin loaded — lossless context management active
2026-04-26 04:44:26,960 INFO hermes_cli.plugins: Plugin discovery complete: 7 found, 7 enabled
2026-04-26 04:44:27,432 INFO gateway.run: Starting Hermes Gateway...
2026-04-26 04:44:27,433 INFO gateway.run: Session storage: /home/axiom/.hermes/sessions
2026-04-26 04:44:27,451 INFO gateway.run: Previous gateway exited cleanly — skipping session suspension
2026-04-26 04:44:27,554 INFO gateway.run: Connecting to telegram...
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram_network: DoH discovery yielded no new IPs (system DNS: 149.154.166.110); using seed fallback IPs 149.154.167.220
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram: [Telegram] Auto-discovered Telegram fallback IPs: 149.154.167.220
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram: [Telegram] Telegram fallback IPs active: 149.154.167.220
2026-04-26 04:44:28,786 INFO apscheduler.scheduler: Scheduler started
2026-04-26 04:44:28,786 INFO telegram.ext.Application: Application started
2026-04-26 04:44:29,891 INFO gateway.platforms.telegram: [Telegram] Telegram menu: 100 commands registered, 34 hidden (over 100 limit). Use /commands for full list.
2026-04-26 04:44:29,891 INFO gateway.platforms.telegram: [Telegram] Connected to Telegram (polling mode)
2026-04-26 04:44:29,892 INFO gateway.run: ✓ telegram connected
2026-04-26 04:44:29,928 INFO gateway.run: Connecting to discord...
2026-04-26 04:44:29,945 INFO gateway.platforms.discord: [Discord] Registered /skill command with 99 skill(s) via autocomplete
2026-04-26 04:44:29,945 INFO gateway.platforms.discord: [Discord] 1 skill(s) filtered out of /skill (name clamp / reserved)
2026-04-26 04:44:29,945 INFO discord.client: logging in using static token
2026-04-26 04:44:31,694 INFO discord.gateway: Shard ID None has connected to Gateway (Session ID: dc752ea312381b982948990c8b7bee0b).
2026-04-26 04:44:33,698 INFO gateway.platforms.discord: [Discord] Connected as Dex#8667
2026-04-26 04:44:33,699 INFO gateway.run: ✓ discord connected
2026-04-26 04:44:33,699 INFO gateway.run: 1 hook(s) loaded
2026-04-26 04:44:33,699 INFO gateway.run: Gateway running with 2 platform(s)
2026-04-26 04:44:33,708 INFO gateway.run: Channel directory built: 3 target(s)
2026-04-26 04:44:33,709 INFO gateway.run: Press Ctrl+C to stop
2026-04-26 04:44:33,721 INFO gateway.run: Cron ticker started (interval=60s)
2026-04-26 04:45:03,701 WARNING gateway.platforms.discord: [Discord] Slash command sync timed out after 30s

---
RAW_BUFFERClick to expand / collapse

Bug Description

When using Qwen3.6 models hosted locally, the agent occasionally gets stuck in an infinite loop when troubleshooting code. This is a new issue with the same models that worked previously. I have tested multiple versions and quants from Unsloth, Bartowski of Qwen3.6 35b A3B and 27b. The only thing that changed in the setup before this started happening was running hermes update, after troubleshooting the updated instance I installed a fresh version of the latest main branch in a vm and had the same results with long running tasks involving file reading.

Steps to Reproduce

  1. Model calls read_file on a file → gets content
  2. Model calls read_file on the same file again → gets dedup stub:
    {"content": "File unchanged since last read. The content from the earlier read_file result in this conversation is still current — refer to that instead of re-reading.", "path": "...", "dedup": true}
  3. Model calls read_file again → same dedup stub (infinite loop)

The existing loop-detection guard at tools/file_tools.py:538 (hard block after 4 consecutive reads) and the warning at line 549 (after 3 consecutive reads) are never reached for dedup stubs because the dedup path returns early at line 447.

Expected Behavior

read_file → content      (dedup mtime cached)
read_file → dedup stub   (dedup_hits[key]=1)
read_file → BLOCKED      (dedup_hits[key]=2 → hard error: "STOP calling read_file")

Actual Behavior

read_file → content      (consecutive=1)
read_file → dedup stub   (dedup path returns early, never hits loop detection)
read_file → dedup stub   (same stub forever)
...

Affected Component

Agent Core (conversation loop, context compression, memory)

Messaging Platform (if gateway-related)

No response

Debug Report

2026-04-26 04:44:26,890 INFO hermes_cli.plugins: Plugin 'openai' registered image_gen provider: openai
2026-04-26 04:44:26,891 INFO hermes_cli.plugins: Plugin 'openai-codex' registered image_gen provider: openai-codex
2026-04-26 04:44:26,921 INFO hermes_cli.plugins: Plugin 'xai' registered image_gen provider: xai
2026-04-26 04:44:26,959 INFO hermes_cli.plugins: Plugin 'hermes-lcm' registered context engine: lcm
2026-04-26 04:44:26,959 INFO hermes_plugins.hermes_lcm: LCM slash command registration disabled (set LCM_ENABLE_SLASH_COMMAND=1 to enable /lcm)
2026-04-26 04:44:26,959 INFO hermes_plugins.hermes_lcm: LCM plugin loaded — lossless context management active
2026-04-26 04:44:26,960 INFO hermes_cli.plugins: Plugin discovery complete: 7 found, 7 enabled
2026-04-26 04:44:27,432 INFO gateway.run: Starting Hermes Gateway...
2026-04-26 04:44:27,433 INFO gateway.run: Session storage: /home/axiom/.hermes/sessions
2026-04-26 04:44:27,451 INFO gateway.run: Previous gateway exited cleanly — skipping session suspension
2026-04-26 04:44:27,554 INFO gateway.run: Connecting to telegram...
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram_network: DoH discovery yielded no new IPs (system DNS: 149.154.166.110); using seed fallback IPs 149.154.167.220
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram: [Telegram] Auto-discovered Telegram fallback IPs: 149.154.167.220
2026-04-26 04:44:27,841 INFO gateway.platforms.telegram: [Telegram] Telegram fallback IPs active: 149.154.167.220
2026-04-26 04:44:28,786 INFO apscheduler.scheduler: Scheduler started
2026-04-26 04:44:28,786 INFO telegram.ext.Application: Application started
2026-04-26 04:44:29,891 INFO gateway.platforms.telegram: [Telegram] Telegram menu: 100 commands registered, 34 hidden (over 100 limit). Use /commands for full list.
2026-04-26 04:44:29,891 INFO gateway.platforms.telegram: [Telegram] Connected to Telegram (polling mode)
2026-04-26 04:44:29,892 INFO gateway.run: ✓ telegram connected
2026-04-26 04:44:29,928 INFO gateway.run: Connecting to discord...
2026-04-26 04:44:29,945 INFO gateway.platforms.discord: [Discord] Registered /skill command with 99 skill(s) via autocomplete
2026-04-26 04:44:29,945 INFO gateway.platforms.discord: [Discord] 1 skill(s) filtered out of /skill (name clamp / reserved)
2026-04-26 04:44:29,945 INFO discord.client: logging in using static token
2026-04-26 04:44:31,694 INFO discord.gateway: Shard ID None has connected to Gateway (Session ID: dc752ea312381b982948990c8b7bee0b).
2026-04-26 04:44:33,698 INFO gateway.platforms.discord: [Discord] Connected as Dex#8667
2026-04-26 04:44:33,699 INFO gateway.run: ✓ discord connected
2026-04-26 04:44:33,699 INFO gateway.run: 1 hook(s) loaded
2026-04-26 04:44:33,699 INFO gateway.run: Gateway running with 2 platform(s)
2026-04-26 04:44:33,708 INFO gateway.run: Channel directory built: 3 target(s)
2026-04-26 04:44:33,709 INFO gateway.run: Press Ctrl+C to stop
2026-04-26 04:44:33,721 INFO gateway.run: Cron ticker started (interval=60s)
2026-04-26 04:45:03,701 WARNING gateway.platforms.discord: [Discord] Slash command sync timed out after 30s

Operating System

Debian 13

Python Version

3.11.15

Hermes Version

v0.11.0 (2026.4.23)

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

The dedup check (lines 422–449) returns early when a file hasn't changed. The loop-detection code (lines 496–554) that tracks consecutive reads and applies hard blocks only runs after a real file read. Dedup stubs bypass all of it.

This means a model that doesn't understand the dedup hint can call read_file on the same path forever, getting the same stub each time, consuming iteration budget until budget exhaustion.

Proposed Fix (optional)

File: tools/file_tools.py

  1. Track dedup hits per key — Added dedup_hits dict to _read_tracker[task_id] that counts how many times each dedup key has been returned. After 2 hits, returns a hard BLOCKED error instead of another stub.

  2. Hardened dedup message — Changed the stub from passive "refer to that instead of re-reading" to explicit "STOP — do not call read_file again for this file."

  3. Cleanupreset_file_dedup() and _cap_read_tracker_data() now also handle dedup_hits.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

extent analysis

TL;DR

The proposed fix involves modifying the tools/file_tools.py file to track dedup hits per key and return a hard BLOCKED error after 2 hits to prevent infinite loops.

Guidance

  • Review the proposed fix in the tools/file_tools.py file, specifically the addition of the dedup_hits dict and the hardened dedup message.
  • Verify that the loop-detection code is correctly tracking consecutive reads and applying hard blocks after 2 dedup hits.
  • Test the modified code with the provided example to ensure it prevents infinite loops.
  • Consider submitting a PR with the proposed fix if it resolves the issue.

Example

# tools/file_tools.py
def _read_file(task_id, path):
    # ...
    if dedup and dedup_hits.get(path, 0) >= 2:
        return "BLOCKED"
    # ...

Notes

The proposed fix assumes that the dedup_hits dict is correctly implemented and that the hardened dedup message is sufficient to prevent models from calling read_file excessively. Additional testing and verification may be necessary to ensure the fix is effective.

Recommendation

Apply the proposed workaround by modifying the tools/file_tools.py file to track dedup hits per key and return a hard BLOCKED error after 2 hits. This should prevent infinite loops and resolve the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING