Skip to content

Edit/write tools enter infinite retry loops when external processes modify files (stale FileTime) #19040

Closed as not planned

Description

@anandgupta42

Problem

When an external process (formatter, linter, file watcher) modifies a file between a read and edit/write call, FileTime.assert() correctly throws "File has been modified since it was last read." However, the model retries the same edit without re-reading the file, creating long retry loops of identical failures.

We've observed bursts of up to 295 consecutive failures in 5-minute windows on the same file, with the model repeatedly attempting the same edit that keeps failing because:

  1. The FileTime read timestamp is never refreshed
  2. The model doesn't see the current file content, so it retries with the same (now stale) oldString

This is particularly common in SQL/dbt workflows where linters like sqlfmt or sqlfluff aggressively reformat files on save.

Proposed fix

Recover at the agent loop level in processor.ts, not at the tool level:

When a tool-error contains "modified since it was last read":

  1. Auto-re-read the file from disk
  2. Update FileTime.read() for the session
  3. Append the fresh file content to the error message fed back to the model

This preserves the safety check (FileTime.assert() still throws) while ensuring the model sees the current file state and can adjust its next edit. This is better than a tool-level auto-refresh (like Gemini CLI does) because:

  • The model's context gets updated — it sees the reformatted content
  • Prevents secondary failures where oldString doesn't match the reformatted file
  • The safety signal is preserved in the error

Reproduction

  1. Open a file with an auto-formatter (e.g., sqlfmt on save)
  2. Ask the model to edit the file
  3. The formatter runs between the read and write
  4. The edit fails with "modified since it was last read"
  5. The model retries the same edit indefinitely

Existing mitigations

The doom loop detector (DOOM_LOOP_THRESHOLD = 3) doesn't catch this because each retry has slightly different error message content (timestamps change), so JSON.stringify(input) differs between retries.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 24, 2026
  2. github-actions commented on Mar 24, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your issue is distinct (e.g., specifically about external processes outside the agent loop causing the stale read, and the retry loop not being caught by the doom loop detector), please clarify how it differs from the above.

  3. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  4. github-actions commented on Jul 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions