oraios/serena · error · ValueError

The content of {path} changed while replacing (occurrence {e

Error message

The content of {path} changed while replacing (occurrence {e} no longer resolves); the file was NOT modified. Re-run with dry_run=True for current ids.

What it means

During _apply_occurrences, Serena re-derives occurrences from the authoritative file content (to tolerate line-ending normalization) and re-validates the previously selected occurrences by id. If an id no longer resolves, the file content changed concurrently; the tool raises this ValueError and leaves the file unmodified rather than producing a corrupted edit.

Source

Thrown at src/serena/tools/file_tools.py:441

        needle: str,
        repl: str,
    ) -> str:
        occurrences_by_file: dict[str, list[ReplacementOccurrence]] = {}
        for occ in occurrences:
            occurrences_by_file.setdefault(occ.relative_path, []).append(occ)
        with self.DiagnosticsContext(self, *occurrences_by_file.keys()) as diagnostics_context:
            code_editor = self.create_code_editor()
            for path, file_occurrences in occurrences_by_file.items():
                with EditedFileContext(path, code_editor) as context:
                    original_content = context.get_original_content()
                    if original_content != contents[path]:
                        # the editor's view differs from what was scanned (e.g. line-ending normalization);
                        # re-derive the occurrences from the authoritative content and re-validate by id
                        fresh_by_id = {o.occurrence_id: o for o in replacer.find_occurrences([(path, original_content)], needle, repl)}
                        try:
                            file_occurrences = [fresh_by_id[o.occurrence_id] for o in file_occurrences]
                        except KeyError as e:
                            raise ValueError(
                                f"The content of {path} changed while replacing (occurrence {e} no longer resolves); "
                                f"the file was NOT modified. Re-run with dry_run=True for current ids."
                            ) from e
                    context.set_updated_content(replacer.apply_to_content(original_content, file_occurrences))
            per_file = "\n".join(f"  {path}: {len(occs)}" for path, occs in occurrences_by_file.items())
            summary = f"Replaced {len(occurrences)} occurrence(s) in {len(occurrences_by_file)} file(s):\n{per_file}"
            return diagnostics_context.format_result(summary)


class DeleteLinesTool(EditingToolWithDiagnostics, ToolMarkerOptional):
    """
    Deletes a range of lines within a file.
    """

    def apply(
        self,
        relative_path: str,
        start_line: int,

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Re-run the whole operation with dry_run=True to obtain current occurrence ids, then apply with the fresh ids
  2. Ensure no formatter/autosave or concurrent process modifies the file during the replacement
  3. Retry after the external writer finishes; re-scan content first to confirm it is stable
  4. Apply replacements serially per file instead of in parallel with other editors

Example fix

// before
agent.replace_content(needle="foo", repl="bar", occurrence_ids=stale_ids)
# ValueError: content changed while replacing...
// after
dry = agent.replace_content(needle="foo", repl="bar", dry_run=True)
agent.replace_content(needle="foo", repl="bar",
                      occurrence_ids=[o.id for o in dry.occurrences])
Defensive patterns

Strategy: retry

Validate before calling

content_before = open(abs_path, 'rb').read()
# ... run replacement ...
content_after = open(abs_path, 'rb').read()
assert content_before == content_after or replacement_succeeded

Type guard

def file_stable(path: str, settle_secs: float = 0.2) -> bool:
    import time
    a = open(path, 'rb').read(); time.sleep(settle_secs)
    return open(path, 'rb').read() == a

Try / catch

try:
    agent.replace_content(needle, repl, occurrence_ids=ids)
except ValueError as e:
    if "changed while replacing" in str(e):
        dry = agent.replace_content(needle, repl, dry_run=True)
        agent.replace_content(needle, repl, occurrence_ids=[o.id for o in dry.occurrences])
    else:
        raise

Prevention

When it happens

Trigger: The file was edited (manually, by an editor with autosave/format-on-save, or by another agent/process) between the scan and the apply; line-ending conversion altered the content beyond what normalization can reconcile; occurrence ids from a stale dry run are replayed against changed content.

Common situations: Agents running replacements while a language server or formatter rewrites files; concurrent automation pipelines touching the same file; applying replacements in a worktree while an IDE buffer is being saved.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/061e3e053afc5e9d. Report an issue: GitHub.