NousResearch/hermes-agent · error · ValueError

old_string and new_string required

Error message

old_string and new_string required

What it means

Raised by _proposal_for_patch_replace when either 'old_string' or 'new_string' is missing (None) from the patch tool-call arguments. Both are needed to render an approval diff and to run fuzzy_find_and_replace. Empty strings are allowed (deletion/insertion edge cases); only absence triggers the error.

Source

Thrown at acp_adapter/edit_approval.py:105

    if content is None:
        raise ValueError("content required")
    return EditProposal(
        tool_name="write_file",
        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",

View on GitHub (pinned to c896c09c42)

Solutions

  1. Always send both old_string and new_string, using '' for the empty side (deletion or insertion).
  2. Normalize field aliases at the ACP boundary if clients use different names.
  3. Fix the agent's tool description to state both fields are required and may be empty strings.
  4. Map null to '' for the intentionally-empty side before validation if your protocol distinguishes null from ''.

Example fix

# before
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")

# after — accept explicit null as empty (delete/insert) semantics
old_string = arguments.get("old_string")
new_string = arguments.get("new_string")
if old_string is None and new_string is None:
    raise ValueError("old_string and new_string required")
old_string = old_string or ""
new_string = new_string or ""
Defensive patterns

Strategy: validation

Validate before calling

old_string = arguments.get("old_string")
new_string = arguments.get("new_string")
if old_string is None or new_string is None:
    return invalid_params("patch requires both old_string and new_string (use '' for empty)")

Type guard

def has_patch_strings(args: dict[str, Any]) -> bool:
    return args.get("old_string") is not None and args.get("new_string") is not None

Try / catch

try:
    proposal = _proposal_for_patch_replace(arguments)
except ValueError as err:
    return {"error": {"code": -32602, "message": str(err)}}

Prevention

When it happens

Trigger: A patch call where the agent supplied old_string but omitted new_string (intending deletion via empty string but sending null instead), or vice versa; schema mismatches renaming the fields to find/replace.

Common situations: LLM agents emitting {path, old_string} for deletes with new_string: null; clients using search/replace naming from other ecosystems (find/replace, match/replacement).

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/7c7744a49371cb41. Report an issue: GitHub.