{"record":{"id":"0409a949e2cd36e6","repo":"invoke-ai/InvokeAI","slug":"image-move-job-job-id-failed-commit-validation","errorCode":null,"errorMessage":"Image move job {job_id} failed commit validation","messagePattern":"Image move job (.+?) failed commit validation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_moves/image_moves_default.py","lineNumber":625,"sourceCode":"            )\n            cursor.execute(\n                \"\"\"--sql\n                SELECT COUNT(*)\n                FROM image_subfolder_move_items AS item\n                LEFT JOIN images ON images.image_name = item.image_name\n                WHERE item.job_id = ?\n                  AND item.state = 'moved'\n                  AND (\n                    images.image_name IS NULL\n                    OR images.deleted_at IS NOT NULL\n                    OR images.image_subfolder != item.new_subfolder\n                  );\n                \"\"\",\n                (job_id,),\n            )\n            invalid_count = cast(int, cursor.fetchone()[0])\n            if invalid_count:\n                raise RuntimeError(f\"Image move job {job_id} failed commit validation\")\n            cursor.execute(\n                \"SELECT COUNT(*) FROM image_subfolder_move_items WHERE job_id = ? AND state = 'moved';\",\n                (job_id,),\n            )\n            moved_count = cast(int, cursor.fetchone()[0])\n            cursor.execute(\n                \"\"\"--sql\n                SELECT error_message\n                FROM image_subfolder_move_items\n                WHERE job_id = ? AND state = 'error'\n                ORDER BY image_name;\n                \"\"\",\n                (job_id,),\n            )\n            error_rows = cursor.fetchall()\n            error_messages = [cast(str, row[0]) for row in error_rows if row[0]]\n            cursor.execute(\n                \"UPDATE image_subfolder_move_items SET state = 'committed' WHERE job_id = ? AND state = 'moved';\",","sourceCodeStart":607,"sourceCodeEnd":643,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_moves/image_moves_default.py#L607-L643","documentation":"Raised by commit_database_updates after running an internal SQL consistency check: if any image_subfolder_move_items rows for the job are in an invalid/inconsistent state (e.g. moved files on disk that don't match the expected pre-commit state), the job fails commit validation and the database update is aborted. This protects against committing a DB state that doesn't match the filesystem.","triggerScenarios":"move_all_images or startup_recovery reaching the commit phase while item rows are in an unexpected state — typically because filesystem completion (_complete_partial_filesystem_move) failed partway, items were manually modified in the DB, or a previous crashed commit left rows half-updated.","commonSituations":"Interrupted earlier recovery run; manual DB edits; concurrent move jobs touching the same items; a prior item-level error (e.g. unreadable image) leaving items non-terminal.","solutions":["Query image_subfolder_move_items for the job and inspect state values to find the invalid rows: SELECT image_name, state FROM image_subfolder_move_items WHERE job_id = ? AND state NOT IN ('moved','skipped','failed');","Re-run startup_recovery so the filesystem and item states are re-reconciled before commit.","Fix item states to terminal values only after verifying the actual file locations on disk.","If the job is hopelessly inconsistent, reset items to 'planned' and re-plan the move after confirming the current file layout."],"exampleFix":"// before: commit aborts due to non-terminal item states\nsqlite3 invokeai.db \"SELECT image_name,state FROM image_subfolder_move_items WHERE job_id=3;\"\n// after: reconcile, then mark verified rows terminal\nsqlite3 invokeai.db \"UPDATE image_subfolder_move_items SET state='moved' WHERE job_id=3 AND image_name='a.png';\"\n# re-run startup_recovery then commit_database_updates","handlingStrategy":"try-catch","validationCode":"import sqlite3\ndef job_is_consistent(db_path: str, job_id: int) -> bool:\n    con = sqlite3.connect(db_path)\n    n = con.execute(\n        \"SELECT COUNT(*) FROM image_subfolder_move_items WHERE job_id=? AND state NOT IN ('moved','skipped','failed')\",\n        (job_id,),\n    ).fetchone()[0]\n    return n == 0","typeGuard":"def item_state_is_terminal(state: str) -> bool:\n    return state in {'moved', 'skipped', 'failed'}","tryCatchPattern":"try:\n    service.commit_database_updates(job_id)\nexcept RuntimeError as e:\n    if \"failed commit validation\" in str(e):\n        # reconcile filesystem vs DB via startup_recovery, then retry once\n        service.startup_recovery()\n        service.commit_database_updates(job_id)","preventionTips":["Always run startup_recovery after crashes before committing/re-running moves","Never edit image_subfolder_move_items rows by hand","Keep move jobs single-threaded per images root","Inspect item states before invoking commit_database_updates"],"tags":["database","image-move","consistency","sqlite"],"backgroundTag":"commit-validation-failed","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}