{"record":{"id":"fd326802d2a9ab2b","repo":"NousResearch/hermes-agent","slug":"no-file-paths-found-in-v4a-patch","errorCode":null,"errorMessage":"no file paths found in V4A patch","messagePattern":"no file paths found in V4A patch","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"acp_adapter/edit_approval.py","lineNumber":162,"sourceCode":"        re.MULTILINE,\n    ):\n        src = match.group(1).strip()\n        dst = match.group(2).strip()\n        if src:\n            paths.append(src)\n        if dst:\n            paths.append(dst)\n    return paths\n\n\ndef _proposal_for_patch_v4a(arguments: dict[str, Any]) -> EditProposal:\n    patch_body = arguments.get(\"patch\")\n    if not isinstance(patch_body, str) or not patch_body:\n        raise ValueError(\"patch content required\")\n\n    paths = _extract_v4a_patch_paths(patch_body)\n    if not paths:\n        raise ValueError(\"no file paths found in V4A patch\")\n\n    proposal_path = paths[0] if len(paths) == 1 else \", \".join(paths)\n    old_text = _read_text_if_exists(paths[0]) if len(paths) == 1 else None\n    return EditProposal(\n        tool_name=\"patch\",\n        path=proposal_path,\n        old_text=old_text,\n        # ACP only supports a single diff payload here.  Surface the exact V4A\n        # patch content before execution so patch-mode calls are permissioned\n        # and denied patches cannot mutate.\n        new_text=patch_body,\n        arguments=dict(arguments),\n    )\n\n\ndef build_edit_proposal(tool_name: str, arguments: dict[str, Any]) -> EditProposal | None:\n    \"\"\"Return an edit proposal for supported file mutation calls.\"\"\"\n","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/acp_adapter/edit_approval.py#L144-L180","documentation":"Raised by _proposal_for_patch_v4a after receiving a syntactically-present patch string from which _extract_v4a_patch_paths could extract no file paths (it scans diff headers like '--- a/...' / '+++ b/...' pairs). With no paths there is no file to attribute the proposal to, so approval cannot proceed. It means the diff body is not in a header format the extractor recognizes — not that the files are missing on disk.","triggerScenarios":"A patch payload in a format whose headers the regex misses: git-style diffs with renamed/broken headers, contextless 'ed'-style or custom diff dialects, a patch whose lines were escaped/quoted so '---'/'+++' no longer match, or hunks concatenated without their file headers.","commonSituations":"Clients sending only hunk bodies (@@ sections) without the ---/+++ header lines; diff producers using 'diff --git' without the following ---/+++ pair (e.g. binary or rename-only diffs); payloads mangled by JSON string escaping or HTML-entity encoding.","solutions":["Ensure the patch includes standard '--- a/<path>' and '+++ b/<path>' header lines before each hunk.","Regenerate the diff with `git diff` / `diff -u` rather than hand-editing, and send it unescaped.","For new-file diffs, include the /dev/null pairing so at least the '+++' path is extracted.","As a maintainer: extend _extract_v4a_patch_paths to also read 'diff --git a/... b/...' lines as a fallback."],"exampleFix":"# before (hunks only, no headers -> 'no file paths found in V4A patch')\n\"@@ -1,3 +1,4 @@\\n context\\n+new line\"\n\n# after (headers included)\n\"--- a/src/app.ts\\n+++ b/src/app.ts\\n@@ -1,3 +1,4 @@\\n context\\n+new line\"","handlingStrategy":"validation","validationCode":"import re\ndef has_diff_file_headers(patch: str) -> bool:\n    return bool(re.search(r'^(---|\\+\\+\\+|diff --git) ', patch, re.MULTILINE))\nif not has_diff_file_headers(patch_body):\n    return invalid_params('patch must include ---/+++ (or diff --git) file headers')","typeGuard":"def is_headered_unified_diff(patch: str) -> bool:\n    return re.search(r'^--- (a/)?\\S+\\n\\+\\+\\+ (b/)?\\S+', patch, re.MULTILINE) is not None","tryCatchPattern":"try:\n    proposal = _proposal_for_patch_v4a(arguments)\nexcept ValueError as err:\n    if 'no file paths' in str(err):\n        # ask the sender to regenerate with `git diff` so headers are present\n        return {\"error\": {\"code\": -32602, \"message\": 'patch missing ---/+++ headers; regenerate with git diff'}}\n    raise","preventionTips":["Generate diffs with git diff / diff -u, never hand-edit","Always include ---/+++ header lines with each hunk","Don't escape or reflow diff bodies in transit"],"tags":["acp","editor-integration","patch","diff-format","validation","python"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}