{"record":{"id":"7c7744a49371cb41","repo":"NousResearch/hermes-agent","slug":"old-string-and-new-string-required","errorCode":null,"errorMessage":"old_string and new_string required","messagePattern":"old_string and new_string required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"acp_adapter/edit_approval.py","lineNumber":105,"sourceCode":"    if content is None:\n        raise ValueError(\"content required\")\n    return EditProposal(\n        tool_name=\"write_file\",\n        path=path,\n        old_text=_read_text_if_exists(path),\n        new_text=str(content),\n        arguments=dict(arguments),\n    )\n\n\ndef _proposal_for_patch_replace(arguments: dict[str, Any]) -> EditProposal:\n    path = str(arguments.get(\"path\") or \"\")\n    if not path:\n        raise ValueError(\"path required\")\n    old_string = arguments.get(\"old_string\")\n    new_string = arguments.get(\"new_string\")\n    if old_string is None or new_string is None:\n        raise ValueError(\"old_string and new_string required\")\n\n    old_text = _read_text_if_exists(path)\n    if old_text is None:\n        raise ValueError(f\"Failed to read file: {path}\")\n\n    from tools.fuzzy_match import fuzzy_find_and_replace\n\n    new_text, match_count, _strategy, error = fuzzy_find_and_replace(\n        old_text,\n        str(old_string),\n        str(new_string),\n        bool(arguments.get(\"replace_all\", False)),\n    )\n    if error or match_count == 0:\n        raise ValueError(error or f\"Could not find match for old_string in {path}\")\n\n    return EditProposal(\n        tool_name=\"patch\",","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/acp_adapter/edit_approval.py#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"LLM agents emitting {path, old_string} for deletes with new_string: null; clients using search/replace naming from other ecosystems (find/replace, match/replacement).","solutions":["Always send both old_string and new_string, using '' for the empty side (deletion or insertion).","Normalize field aliases at the ACP boundary if clients use different names.","Fix the agent's tool description to state both fields are required and may be empty strings.","Map null to '' for the intentionally-empty side before validation if your protocol distinguishes null from ''."],"exampleFix":"# before\nold_string = arguments.get(\"old_string\")\nnew_string = arguments.get(\"new_string\")\nif old_string is None or new_string is None:\n    raise ValueError(\"old_string and new_string required\")\n\n# after — accept explicit null as empty (delete/insert) semantics\nold_string = arguments.get(\"old_string\")\nnew_string = arguments.get(\"new_string\")\nif old_string is None and new_string is None:\n    raise ValueError(\"old_string and new_string required\")\nold_string = old_string or \"\"\nnew_string = new_string or \"\"","handlingStrategy":"validation","validationCode":"old_string = arguments.get(\"old_string\")\nnew_string = arguments.get(\"new_string\")\nif old_string is None or new_string is None:\n    return invalid_params(\"patch requires both old_string and new_string (use '' for empty)\")","typeGuard":"def has_patch_strings(args: dict[str, Any]) -> bool:\n    return args.get(\"old_string\") is not None and args.get(\"new_string\") is not None","tryCatchPattern":"try:\n    proposal = _proposal_for_patch_replace(arguments)\nexcept ValueError as err:\n    return {\"error\": {\"code\": -32602, \"message\": str(err)}}","preventionTips":["State in the tool description: both fields required, empty string legal for delete/insert","Map null to '' explicitly if your producer distinguishes them","Validate at the boundary so agents get actionable invalid-params errors"],"tags":["acp","editor-integration","validation","patch","python"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}