hermes - ✅(Solved) Fix [Bug]: read_file dedup cache not invalidated by write_file on same path — returns stale "not found" after successful write [1 pull requests]

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…

Error Message

silently corrupts any write-then-read workflow in the same session. Agent sees stale state and enters error-recovery loops. In the attached session transcript the agent burned 12 API calls and ~$0.03 fighting this cache, eventually working around it with terminal("cat hello.txt"). read_file(P) returns {"content": "File unchanged since last read...", "path": "P", "dedup": true} in 0.00s, serving the pre-write result (including a stale "File not found" error). write_file does not invalidate the read_file cache entry for the same path.

Additional Logs / Traceback (optional)

20:25:25 - run_agent - WARNING [20260420_202441_6227d1] - Tool read_file returned error (0.43s): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"} 20:25:25 - root - DEBUG [20260420_202441_6227d1] - Tool result (146 chars): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"} The dedup layer caches read_file results by (path, session) without listening for write_file / patch / terminal mutations on the same path, and without stat-checking mtime/size on serve. A second smaller issue: the cache treats error results (File not found) as cacheable, which is independently questionable — errors should generally bypass or short-TTL the cache. Don't cache error results at all, or cache them with a short TTL (e.g. 2 seconds) so transient File not found doesn't poison the session.

Root Cause

Start a Hermes session. Prompt the agent: Create hello.txt with "Hello, world!", then verify with read_file. In the execution, ensure read_file is called for a path before write_file creates it. (In the original repro this happened naturally because bug #1 made execute_code silently no-op, so read_file ran first and got File not found. The same dedup behavior should reproduce whenever the first read_file returns any result — including errors — that the cache then serves forever.)

Fix Action

Fix / Workaround

The dedup layer caches read_file results by (path, session) without listening for write_file / patch / terminal mutations on the same path, and without stat-checking mtime/size on serve. A second smaller issue: the cache treats error results (File not found) as cacheable, which is independently questionable — errors should generally bypass or short-TTL the cache.

Invalidate the read_file dedup entry for path P whenever write_file(P), patch(P), or any mutating filesystem tool runs in the session. Alternatively, key the cache on (path, mtime, size) and stat on serve — cheap (~microseconds) and makes the cache self-healing. Don't cache error results at all, or cache them with a short TTL (e.g. 2 seconds) so transient File not found doesn't poison the session.

PR fix notes

PR #13207: fix(tools): invalidate read_file dedup cache on write_file and patch

Description (problem / solution / changelog)

Summary

write_file_tool and patch_tool do not invalidate the read_file dedup cache for the written path. This causes stale content (or stale "File not found" errors) to be returned on subsequent read_file calls.

Fixes #13144

Root Cause

The dedup cache maps (resolved_path, offset, limit) → mtime. After a write, _update_read_timestamp refreshes the staleness tracker but leaves the dedup dict untouched.

On filesystems where the read and write land in the same mtime second (or with ≥1s mtime granularity), the cached and current mtime are equal → dedup check passes → read_file returns a stale "File unchanged since last read" stub.

The reporter observed the agent burning 12 API calls (~$0.03) fighting this cache, eventually working around it with terminal("cat hello.txt").

Fix

Add _invalidate_dedup_for_path(filepath, task_id) — removes all dedup entries whose resolved path matches the written file (across all offset/limit combinations). Called from _update_read_timestamp so both write_file_tool and patch_tool benefit automatically.

Scoped to the writing task_id — other tasks' caches are not affected.

Changes

FileLinesDescription
tools/file_tools.py+35Add _invalidate_dedup_for_path, call from _update_read_timestamp
tests/tools/test_file_read_guards.py+1716 regression tests

Tests

6 new tests covering:

  1. Core scenario: read→write→read within same mtime second returns fresh content
  2. All offsets invalidated: writing invalidates dedup for ALL offset/limit combos
  3. File isolation: writing file A does not invalidate file B's cache
  4. Task isolation: writing in task A does not invalidate task B's cache
  5. Safety: _invalidate_dedup_for_path is no-op when task_id does not exist
  6. Safety: _invalidate_dedup_for_path is no-op when dedup dict is empty

All 25 tests pass (19 existing + 6 new):

======================== 25 passed, 4 warnings in 1.73s ========================

Changed files

  • tests/tools/test_file_read_guards.py (modified, +171/-0)
  • tools/file_tools.py (modified, +35/-0)

Code Example

Report     https://paste.rs/TYMHN
  agent.log  https://paste.rs/MiglU

---

20:25:25 - run_agent - WARNING [20260420_202441_6227d1] - Tool read_file returned error (0.43s): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"}
20:25:25 - root - DEBUG [20260420_202441_6227d1] - Tool read_file completed in 0.43s
20:25:25 - root - DEBUG [20260420_202441_6227d1] - Tool result (146 chars): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"}

[... agent decides to use write_file directly ...]

20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool call: write_file with args: {"path":"hello.txt","content":"Hello, world!"}...
20:25:42 - tools.checkpoint_manager - DEBUG [20260420_202441_6227d1] - Checkpoint skipped: directory too broad (/home/ubuntu)
20:25:42 - run_agent - INFO [20260420_202441_6227d1] - tool write_file completed (0.41s, 44 chars)
20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool write_file completed in 0.41s
20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool result (44 chars): {"bytes_written": 13, "dirs_created": false}

[... agent immediately calls read_file on same path ...]

20:25:43 - root - DEBUG [20260420_202441_6227d1] - Tool call: read_file with args: {"p
RAW_BUFFERClick to expand / collapse

Bug Description

silently corrupts any write-then-read workflow in the same session. Agent sees stale state and enters error-recovery loops. In the attached session transcript the agent burned 12 API calls and ~$0.03 fighting this cache, eventually working around it with terminal("cat hello.txt").

Steps to Reproduce

Start a Hermes session. Prompt the agent: Create hello.txt with "Hello, world!", then verify with read_file. In the execution, ensure read_file is called for a path before write_file creates it. (In the original repro this happened naturally because bug #1 made execute_code silently no-op, so read_file ran first and got File not found. The same dedup behavior should reproduce whenever the first read_file returns any result — including errors — that the cache then serves forever.)

Expected Behavior

After write_file succeeds on path P, the next read_file(P) reflects the new content.

Actual Behavior

read_file(P) returns {"content": "File unchanged since last read...", "path": "P", "dedup": true} in 0.00s, serving the pre-write result (including a stale "File not found" error). write_file does not invalidate the read_file cache entry for the same path.

Affected Component

Setup / Installation, Tools (terminal, file ops, web, code execution, etc.)

Messaging Platform (if gateway-related)

No response

Debug Report

Report     https://paste.rs/TYMHN
  agent.log  https://paste.rs/MiglU

Operating System

Linux 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Python Version

3.11

Hermes Version

0.10

Additional Logs / Traceback (optional)

20:25:25 - run_agent - WARNING [20260420_202441_6227d1] - Tool read_file returned error (0.43s): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"}
20:25:25 - root - DEBUG [20260420_202441_6227d1] - Tool read_file completed in 0.43s
20:25:25 - root - DEBUG [20260420_202441_6227d1] - Tool result (146 chars): {"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: hello.txt"}

[... agent decides to use write_file directly ...]

20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool call: write_file with args: {"path":"hello.txt","content":"Hello, world!"}...
20:25:42 - tools.checkpoint_manager - DEBUG [20260420_202441_6227d1] - Checkpoint skipped: directory too broad (/home/ubuntu)
20:25:42 - run_agent - INFO [20260420_202441_6227d1] - tool write_file completed (0.41s, 44 chars)
20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool write_file completed in 0.41s
20:25:42 - root - DEBUG [20260420_202441_6227d1] - Tool result (44 chars): {"bytes_written": 13, "dirs_created": false}

[... agent immediately calls read_file on same path ...]

20:25:43 - root - DEBUG [20260420_202441_6227d1] - Tool call: read_file with args: {"p

Root Cause Analysis (optional)

No response

Proposed Fix (optional)

The dedup layer caches read_file results by (path, session) without listening for write_file / patch / terminal mutations on the same path, and without stat-checking mtime/size on serve. A second smaller issue: the cache treats error results (File not found) as cacheable, which is independently questionable — errors should generally bypass or short-TTL the cache.

Invalidate the read_file dedup entry for path P whenever write_file(P), patch(P), or any mutating filesystem tool runs in the session. Alternatively, key the cache on (path, mtime, size) and stat on serve — cheap (~microseconds) and makes the cache self-healing. Don't cache error results at all, or cache them with a short TTL (e.g. 2 seconds) so transient File not found doesn't poison the session.

Search for the string "File unchanged since last read" — that's the dedup sentinel message and will land directly on the relevant code.

Are you willing to submit a PR for this?

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

extent analysis

TL;DR

Invalidate the read_file dedup entry for a path whenever write_file, patch, or any mutating filesystem tool runs in the session to fix the cache corruption issue.

Guidance

  • Identify the cache invalidation logic in the read_file and write_file functions to ensure that the cache is updated after a file is written.
  • Consider implementing a cache key based on (path, mtime, size) to make the cache self-healing.
  • Review the error handling in the cache to prevent caching error results or use a short TTL (e.g., 2 seconds) to avoid poisoning the session.
  • Search for the string "File unchanged since last read" to locate the relevant code and apply the necessary fixes.

Example

No code snippet is provided as the issue does not contain sufficient information about the implementation details.

Notes

The proposed fix suggests multiple approaches to address the issue, including cache invalidation, self-healing cache, and error handling. The choice of approach depends on the specific requirements and constraints of the system.

Recommendation

Apply the workaround by invalidating the read_file dedup entry for a path whenever write_file, patch, or any mutating filesystem tool runs in the session, as this approach directly addresses the root cause of 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