{"record":{"id":"e983aa1338759c8e","repo":"invoke-ai/InvokeAI","slug":"image-move-image-name-already-has-an-active-imag","errorCode":null,"errorMessage":"Image {move.image_name} already has an active image move job","messagePattern":"Image (.+?) already has an active image move job","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_moves/image_moves_default.py","lineNumber":445,"sourceCode":"        destinations: set[Path] = set()\n        thumbnail_destinations: set[Path] = set()\n        for move in moves:\n            if not move.old_path.exists():\n                if not move.is_intermediate:\n                    raise FileNotFoundError(f\"Source image does not exist: {move.old_path}\")\n                continue\n            if move.new_path.exists():\n                raise FileExistsError(f\"Destination image already exists: {move.new_path}\")\n            if move.old_path == move.new_path:\n                raise ValueError(f\"Old and new paths are identical for {move.image_name}\")\n            if move.new_path in destinations:\n                raise ValueError(f\"Duplicate destination path: {move.new_path}\")\n            destinations.add(move.new_path)\n            if move.new_thumbnail_path in thumbnail_destinations:\n                raise ValueError(f\"Duplicate destination thumbnail path: {move.new_thumbnail_path}\")\n            thumbnail_destinations.add(move.new_thumbnail_path)\n            if self._has_active_job_for_image(move.image_name):\n                raise ValueError(f\"Image {move.image_name} already has an active image move job\")\n            self._assert_same_filesystem(move.old_path, move.new_path)\n            if move.old_thumbnail_path.exists():\n                if move.new_thumbnail_path.exists():\n                    raise FileExistsError(f\"Destination thumbnail already exists: {move.new_thumbnail_path}\")\n                self._assert_same_filesystem(move.old_thumbnail_path, move.new_thumbnail_path)\n\n    def _record_missing_source_errors(self, moves: Sequence[PlannedImageMove]) -> tuple[list[PlannedImageMove], int]:\n        remaining_moves: list[PlannedImageMove] = []\n        errors = 0\n        for move in moves:\n            if move.old_path.exists() or move.is_intermediate:\n                remaining_moves.append(move)\n                continue\n            message = f\"Source image does not exist: {move.old_path}\"\n            self.create_error_move_job(move, message)\n            self._logger.error(message)\n            errors += 1\n        return remaining_moves, errors","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_moves/image_moves_default.py#L427-L463","documentation":"An image may participate in only one active (non-terminal) move job at a time. preflight_moves consults _has_active_job_for_image(image_name) and raises ValueError if this image is already listed in another planned/running job, preventing conflicting concurrent moves of the same file.","triggerScenarios":"Re-adding an image to a new batch while a previous job containing it is still 'planned'/'running'; concurrent API requests both planning a move for the same image; a stale non-terminal job from a crash still listing the image.","commonSituations":"Users queuing the same image for two boards; retrying a move without waiting for the first job to finish; crashed jobs never finalized via startup recovery.","solutions":["Wait for the existing job to reach 'committed' or 'error' before planning again","Run startup_recovery to finalize the stale job, then retry","Remove the image from the active job or mark that job 'error' if its outcome is known"],"exampleFix":"// before\nservice.preflight_moves(moves)\n// after\nif any(service.has_active_job_for(m.image_name) for m in moves):\n    raise RuntimeError(\"image already in an active move job; wait or run recovery\")\nservice.preflight_moves(moves)","handlingStrategy":"validation","validationCode":"busy = [m.image_name for m in moves if service.has_active_job_for_image(m.image_name)]\nif busy:\n    raise ValueError(f\"images already in active jobs: {busy}\")\nservice.preflight_moves(moves)","typeGuard":"def image_is_free(image_name: str, active_names: set[str]) -> bool:\n    return image_name not in active_names","tryCatchPattern":"try:\n    service.preflight_moves(moves)\nexcept ValueError as e:\n    if \"already has an active image move job\" in str(e):\n        wait_for_active_jobs()  # poll until committed/error\n        service.preflight_moves(moves)\n    else:\n        raise","preventionTips":["Never queue the same image into two overlapping batches","Run startup recovery so crashed jobs don't linger as active","Check per-image active-job status in the UI before allowing re-queue"],"tags":["concurrency","image-move","job-state","preflight"],"backgroundTag":"concurrent-operation-conflict","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}