{"record":{"id":"0e13ae632ed93993","repo":"invoke-ai/InvokeAI","slug":"both-old-and-new-image-files-exist-for-item-image","errorCode":null,"errorMessage":"Both old and new image files exist for {item.image_name}","messagePattern":"Both old and new image files exist for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_moves/image_moves_default.py","lineNumber":499,"sourceCode":"                if not self._is_unrecoverable_error(e):\n                    raise\n                self._reconcile_destination_subfolder(item)\n                self.mark_item_unrecoverable(job_id, item.image_name, f\"{item.image_name}: {e}\")\n                self._logger.error(\"Image move skipped unrecoverable item %s: %s\", item.image_name, e)\n\n    def _complete_partial_filesystem_move(self, job_id: int, item: PlannedImageMove) -> None:\n        old_path = self.image_files.get_path(item.image_name, image_subfolder=item.old_subfolder)\n        new_path = self.image_files.get_path(item.image_name, image_subfolder=item.new_subfolder)\n        old_thumbnail_path = self.image_files.get_path(\n            item.image_name, thumbnail=True, image_subfolder=item.old_subfolder\n        )\n        new_thumbnail_path = self.image_files.get_path(\n            item.image_name, thumbnail=True, image_subfolder=item.new_subfolder\n        )\n        old_exists = old_path.exists()\n        new_exists = new_path.exists()\n        if old_exists and new_exists:\n            raise RuntimeError(f\"Both old and new image files exist for {item.image_name}\")\n        if not old_exists and not new_exists:\n            if item.is_intermediate:\n                self._mark_missing_intermediate_moved(\n                    job_id=job_id,\n                    image_name=item.image_name,\n                    old_path=old_path,\n                    new_path=new_path,\n                    old_thumbnail_path=old_thumbnail_path,\n                    new_thumbnail_path=new_thumbnail_path,\n                )\n                return\n            raise RuntimeError(f\"Neither old nor new image file exists for {item.image_name}\")\n\n        old_thumbnail_exists = old_thumbnail_path.exists()\n        new_thumbnail_exists = new_thumbnail_path.exists()\n        if (\n            old_exists\n            and not new_exists","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_moves/image_moves_default.py#L481-L517","documentation":"Raised by _complete_partial_filesystem_move when, before committing a batch image move, both the old and new image file paths exist on disk. This is an ambiguous state — the move may have been partially executed plus a duplicate copy left behind — so the code refuses to guess which file is authoritative and aborts that item with a RuntimeError instead of silently overwriting data.","triggerScenarios":"Completing a partially-applied subfolder move (complete_partial_filesystem_moves) where a previous run already copied/moved the image to item.new_subfolder but the source at old_subfolder was not removed (e.g. a crash between copy and delete, or a manual re-copy of the file into the old location).","commonSituations":"Crash or power loss mid-move leaving duplicate files; a user manually copying the image back to its original folder after a partial move; running the move job twice with a strategy that resolved the same destination; backups/restores that recreate the old path.","solutions":["Manually inspect and delete the stale duplicate (usually the old-path copy is the leftover; verify checksums against the DB record first) then re-run startup_recovery / complete_partial_filesystem_moves.","Check job item state in image_subfolder_move_items; if the item is already 'moved', remove the duplicate old file rather than re-running the move.","If the new file is corrupt/truncated from an interrupted copy, delete the new file so only old_exists and let the move re-execute.","Restore the directory from backup to a consistent single-copy state, then re-run the move job."],"exampleFix":"// before (ambiguous state, move aborts)\n# images/old/abc.png AND images/new/abc.png both present\n// after (resolve duplicate, keep the verified good copy)\n# verify checksums, then:\nrm images/old/abc.png\nrm images/old/abc.webp  # stale thumbnail if present\n# re-run recovery\ninvokeai-move-recovery  # or service.complete_partial_filesystem_moves(job_id)","handlingStrategy":"validation","validationCode":"from pathlib import Path\nold = Path(old_subfolder_dir) / image_name\nnew = Path(new_subfolder_dir) / image_name\nif old.exists() and new.exists():\n    raise RuntimeError(f\"resolve duplicate before completing move: {image_name}\")","typeGuard":"def has_no_duplicate_move(old: Path, new: Path) -> bool:\n    return not (old.exists() and new.exists())","tryCatchPattern":"try:\n    service.complete_partial_filesystem_moves(job_id)\nexcept RuntimeError as e:\n    if \"Both old and new image files exist\" in str(e):\n        # resolve the duplicate manually, then re-run\n        ...","preventionTips":["Never manually copy images back into their source subfolder while a move job is pending","Let startup_recovery run after any crash before issuing new move jobs","Verify job state via get_job before re-running moves","Avoid running concurrent move jobs on the same images root"],"tags":["filesystem","image-move","duplicate-file","runtime"],"backgroundTag":"move-conflict-both-files-exist","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}