NousResearch/hermes-agent · error · ValueError
Failed to read file
Error message
Failed to read file: {path} What it means
Raised by _proposal_for_patch_replace when _read_text_if_exists(path) returns None — meaning Path(path).expanduser() does not exist. A search/replace patch can only apply to an existing file (unlike write_file, which can create one), so the proposal builder refuses to continue when the target is absent. This fires before the fuzzy matcher, distinguishing 'file missing' from 'match not found'.
Solutions
- Verify the file exists from the adapter's working directory; correct the path or create the file with write_file first.
- Use absolute paths in patch calls to avoid cwd-relative mismatches.
- If the file was deleted intentionally, recreate it with write_file before patching.
- Catch this ValueError in the dispatcher and reply 'file not found' so the agent can self-correct.
Example fix
# before
old_text = _read_text_if_exists(path)
if old_text is None:
raise ValueError(f"Failed to read file: {path}")
# after — distinguish missing vs unreadable for the agent
old_text = _read_text_if_exists(path)
if old_text is None:
if not Path(path).expanduser().exists():
raise ValueError(f"File does not exist: {path}")
raise ValueError(f"Failed to read file: {path}") Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
if not Path(path).expanduser().exists():
return error_response(f"File not found: {path} — create it with write_file first or fix the path") Type guard
def file_exists(path: str) -> bool:
return Path(path).expanduser().is_file() Try / catch
try:
proposal = _proposal_for_patch_replace(arguments)
except ValueError as err:
if "Failed to read file" in str(err):
# instruct the agent to re-check the path or use write_file to create
return {"error": {"code": -32602, "message": str(err)}}
raise Prevention
- Use absolute paths in patch calls to avoid cwd-relative misses
- Create new files with write_file, never patch
- Re-verify existence after long-running turns where files may have changed
When it happens
Trigger: A patch call targeting a file that was never created, was deleted, or whose path is wrong (typo, wrong relative root, ~ not expanded by the caller — note expanduser() here handles it, but a wrong cwd-relative path still misses).
Common situations: Agents patching files by remembered paths after the files were renamed; relative paths resolved from a different working directory than the agent assumed; first-edit attempts on files the agent intended to create with patch instead of write_file.
Related errors
- Cannot edit non-file path
- '}
- no file paths found in V4A patch
- old_string and new_string required
- patch content required
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/4fce72acbd724885.
Report an issue: GitHub.
Appendix: source
Thrown at acp_adapter/edit_approval.py:109
path=path,
old_text=_read_text_if_exists(path),
new_text=str(content),
arguments=dict(arguments),
)
def _proposal_for_patch_replace(arguments: dict[str, Any]) -> EditProposal:
path = str(arguments.get("path") or "")
if not path:
raise ValueError("path required")
old_string = arguments.get("old_string")
new_string = arguments.get("new_string")
if old_string is None or new_string is None:
raise ValueError("old_string and new_string required")
old_text = _read_text_if_exists(path)
if old_text is None:
raise ValueError(f"Failed to read file: {path}")
from tools.fuzzy_match import fuzzy_find_and_replace
new_text, match_count, _strategy, error = fuzzy_find_and_replace(
old_text,
str(old_string),
str(new_string),
bool(arguments.get("replace_all", False)),
)
if error or match_count == 0:
raise ValueError(error or f"Could not find match for old_string in {path}")
return EditProposal(
tool_name="patch",
path=path,
old_text=old_text,
new_text=new_text,
arguments=dict(arguments),View on GitHub (pinned to c896c09c42)