{"record":{"id":"e1bd1dced45fea3a","repo":"invoke-ai/InvokeAI","slug":"duplicate-destination-thumbnail-path-move-new-th","errorCode":null,"errorMessage":"Duplicate destination thumbnail path: {move.new_thumbnail_path}","messagePattern":"Duplicate destination thumbnail path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_moves/image_moves_default.py","lineNumber":442,"sourceCode":"            return job_id\n\n    def preflight_moves(self, moves: Sequence[PlannedImageMove]) -> None:\n        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)","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_moves/image_moves_default.py#L424-L460","documentation":"Analogous to duplicate image destinations, two moves in one batch must not claim the same thumbnail destination. preflight_moves tracks new_thumbnail_path values and raises ValueError on a repeat, since each image's thumbnail must land in a unique file.","triggerScenarios":"Two distinct images whose planned thumbnail filenames collide (same name into same folder); constructing PlannedImageMove with hand-built thumbnail paths that repeat; batch merging folders with duplicate names where thumbnail naming derives only from the image name.","commonSituations":"Merging subfolders with identically named images; custom thumbnail-path generation ignoring the source directory; automated scripts generating moves for many folders into one.","solutions":["Ensure thumbnail destinations are derived from unique destination image paths","De-duplicate or suffix colliding thumbnail paths when planning","Filter/split batches so each new_thumbnail_path is unique before preflight"],"exampleFix":"// before\nnew_thumb = thumb_dir / img.name  # collides for duplicate names\n// after\nnew_thumb = (dest_dir / img.name).with_suffix('.thumb.png')  # unique per destination\n","handlingStrategy":"validation","validationCode":"thumbs = [m.new_thumbnail_path for m in moves]\ndupes = {t for t in thumbs if thumbs.count(t) > 1}\nif dupes:\n    raise ValueError(f\"duplicate thumbnail destinations: {dupes}\")\nservice.preflight_moves(moves)","typeGuard":"def thumbnail_destinations_unique(moves: Sequence[PlannedImageMove]) -> bool:\n    return len({m.new_thumbnail_path for m in moves}) == len(moves)","tryCatchPattern":"try:\n    service.preflight_moves(moves)\nexcept ValueError as e:\n    if \"Duplicate destination thumbnail path\" in str(e):\n        moves = rebuild_thumbnail_paths(moves)\n        service.preflight_moves(moves)\n    else:\n        raise","preventionTips":["Derive thumbnail destinations from the unique destination image path, never from the bare filename","De-duplicate thumbnail paths alongside image paths","Test batch planning against folders containing duplicate image names"],"tags":["validation","image-move","thumbnail","duplicate"],"backgroundTag":"duplicate-destination-path","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}