{"record":{"id":"1d73f15f20b1d442","repo":"Aider-AI/aider","slug":"conflicting-actions-for-file-path","errorCode":null,"errorMessage":"Conflicting actions for file: {path}","messagePattern":"Conflicting actions for file: (.+?)","errorType":"exception","errorClass":"DiffError","httpStatus":null,"severity":"error","filePath":"aider/coders/patch_coder.py","lineNumber":333,"sourceCode":"\n                # Optional move target\n                move_to = None\n                if index < len(lines) and _norm(lines[index]).startswith(\"*** Move to: \"):\n                    move_to = _norm(lines[index])[len(\"*** Move to: \") :].strip()\n                    index += 1\n                    if not move_to:\n                        raise DiffError(\"Move to action missing path.\")\n\n                if path not in current_files:\n                    raise DiffError(f\"Update File Error - missing file content for: {path}\")\n\n                file_content = current_files[path]\n\n                existing_action = patch.actions.get(path)\n                if existing_action is not None:\n                    # Merge additional UPDATE block into the existing one\n                    if existing_action.type != ActionType.UPDATE:\n                        raise DiffError(f\"Conflicting actions for file: {path}\")\n\n                    new_action, index, fuzz = self._parse_update_file_sections(\n                        lines, index, file_content\n                    )\n                    existing_action.chunks.extend(new_action.chunks)\n\n                    if move_to:\n                        if existing_action.move_path and existing_action.move_path != move_to:\n                            raise DiffError(f\"Conflicting move targets for file: {path}\")\n                        existing_action.move_path = move_to\n                    fuzz_accumulator += fuzz\n                else:\n                    # First UPDATE block for this file\n                    action, index, fuzz = self._parse_update_file_sections(\n                        lines, index, file_content\n                    )\n                    action.path = path\n                    action.move_path = move_to","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/patch_coder.py#L315-L351","documentation":"Raised by PatchCoder._parse_patch_text when a second action block references a path that already has an action in patch.actions whose type is not UPDATE — e.g. the patch deletes a file and then tries to update it, or adds it and then updates it. Only UPDATE actions may be merged across repeated blocks.","triggerScenarios":"A single patch containing '*** Delete File: foo.py' (or '*** Add File: foo.py') followed later by '*** Update File: foo.py' — the existing_action.type check (≠ ActionType.UPDATE) trips and raises.","commonSituations":"Models emitting delete-then-recreate sequences as delete+update instead of a single Add or Move; long patches where the model loses track of which files it already handled; hallucinated duplicate file sections.","solutions":["Split the intent: use '*** Move to:' on the update block for renames, or a single '*** Add File:' for recreate-from-scratch — never combine Delete/Add with Update for the same path in one patch.","Remove the conflicting earlier action for that path.","Re-emit the patch with each file appearing under exactly one action type.","Feed the error back to the LLM so it restructures the patch."],"exampleFix":"# before\n*** Delete File: foo.py\n*** Update File: foo.py\n@@\n-a\n+b\n*** End Patch\n\n# after\n*** Update File: foo.py\n@@\n-a\n+b\n*** End Patch","handlingStrategy":"validation","validationCode":"from collections import defaultdict\n\ndef conflicting_file_actions(patch_text: str) -> dict[str, set[str]]:\n    acts = defaultdict(set)\n    for l in patch_text.splitlines():\n        s = l.strip()\n        for kind, marker in ((\"ADD\", \"*** Add File: \"), (\"DEL\", \"*** Delete File: \"), (\"UPD\", \"*** Update File: \")):\n            if s.startswith(marker):\n                acts[s[len(marker):].strip()].add(kind)\n    return {p: k for p, k in acts.items() if len(k) > 1}  # non-empty → restructure before applying","typeGuard":null,"tryCatchPattern":"try:\n    coder.get_edits(patch_text)\nexcept ValueError as e:\n    if \"Conflicting actions for file:\" in str(e):\n        path = str(e).split(\"Conflicting actions for file:\")[-1].strip()\n        patch_text = drop_conflicting_action(patch_text, path)  # keep exactly one action type\n        coder.get_edits(patch_text)\n    else:\n        raise","preventionTips":["Give each file exactly one action type (Add, Delete, or Update) per patch — use Move-to for renames.","Lint generated patches for per-path action sets before applying.","When concatenating patches from multiple requests, merge update hunks instead of stacking delete+update."],"tags":["aider","patch-format","parsing","conflicting-actions"],"backgroundTag":null,"analyzedSha":"5dc9490bb35f9729ef2c95d00a19ccd30c26339c","analyzedAt":"2026-08-15T05:40:10.498Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}