{"record":{"id":"856b4885bdf37530","repo":"invoke-ai/InvokeAI","slug":"imagefiledeleteexception","errorCode":null,"errorMessage":"ImageFileDeleteException","messagePattern":"ImageFileDeleteException","errorType":"exception","errorClass":"ImageFileDeleteException","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_files/image_files_disk.py","lineNumber":230,"sourceCode":"            with open(staging_dir / \"manifest.json\", \"w\", encoding=\"utf-8\") as manifest:\n                manifest.write(json.dumps({\"image_name\": image_name, \"image_subfolder\": image_subfolder}))\n                manifest.flush()\n                os.fsync(manifest.fileno())\n            for index, source in enumerate(candidates):\n                with self.__cache_lock:\n                    self.__cache.pop(source, None)\n                if source.exists():\n                    destination = staging_dir / str(index)\n                    source.replace(destination)\n                    staged.append((source, destination))\n            return _StagedDelete(directory=staging_dir, files=staged)\n        except Exception as e:\n            for source, destination in reversed(staged):\n                if destination.exists():\n                    source.parent.mkdir(parents=True, exist_ok=True)\n                    destination.replace(source)\n            shutil.rmtree(staging_dir, ignore_errors=True)\n            raise ImageFileDeleteException from e\n\n    def commit_delete(self, token: object) -> None:\n        if not isinstance(token, _StagedDelete):\n            raise ImageFileDeleteException(\"Invalid staged-delete token\")\n        try:\n            shutil.rmtree(token.directory)\n        except Exception as e:\n            raise ImageFileDeleteException from e\n\n    def rollback_delete(self, token: object) -> None:\n        if not isinstance(token, _StagedDelete):\n            raise ImageFileDeleteException(\"Invalid staged-delete token\")\n        try:\n            for source, destination in reversed(token.files):\n                if destination.exists():\n                    source.parent.mkdir(parents=True, exist_ok=True)\n                    destination.replace(source)\n            shutil.rmtree(token.directory, ignore_errors=True)","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_files/image_files_disk.py#L212-L248","documentation":"ImageFilesDisk.stage_delete() performs a two-phase delete: files are moved into a staging directory, and if any move fails the already-staged files are rolled back (restored in reverse order), the staging directory is removed, and this ImageFileDeleteException is raised with the original error chained. It means the delete could not even be prepared; the original files are left intact.","triggerScenarios":"Calling stage_delete (or delete(), which wraps it) when moving the image or thumbnail into the staging directory raises — e.g. the source file was already removed, or destination/source paths are unwritable or locked.","commonSituations":"Image file deleted externally between API call and staging; permission problems on output or staging directories; file held open/locked by another process on some platforms; inconsistent subfolder layout after manual file moves.","solutions":["Check the chained cause to see which file operation failed and whether the source file still exists","Fix permissions on the output folder and its parent so staging moves can succeed","If the file is already gone, clear the stale record instead of calling delete again","Ensure no external process (sync/backup tooling) is racing with the delete"],"exampleFix":"// before\nservices.images.delete(image_name)  # may raise raw OSError\n// after\nfrom invokeai.app.services.image_files.image_files_common import ImageFileDeleteException\ntry:\n    services.images.delete(image_name)\nexcept ImageFileDeleteException as e:\n    logger.warning(f\"delete failed, file left intact: {e.__cause__}\")","handlingStrategy":"try-catch","validationCode":"from pathlib import Path\np = Path(services.images.get_path(image_name, image_subfolder=subfolder))\nif not p.exists():\n    raise FileNotFoundError(f\"cannot delete, file already gone: {p}\")","typeGuard":null,"tryCatchPattern":"try:\n    services.images.delete(image_name)\nexcept ImageFileDeleteException as e:\n    logger.warning(f\"delete aborted, files left intact: {e.__cause__}\")","preventionTips":["Confirm the image exists before deleting to avoid racing with external removals","Keep permissions consistent on output and staging directories","Pause backup/sync tools that lock output files during maintenance deletes"],"tags":["storage","file-delete","image-files","two-phase-delete"],"backgroundTag":"file-delete-failed","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}