invoke-ai/InvokeAI · error

Old and new paths are identical for

Error message

Old and new paths are identical for {move.image_name}

What it means

A move where old_path == new_path is a no-op and likely a naming/planning bug, so preflight_moves raises ValueError identifying the offending image_name. It only fires when the source exists (checked earlier) and the path isn't already taken by another file check ordering.

Solutions

  1. In the caller, skip or filter moves whose resolved old_path equals new_path (treat as success/no-op)
  2. Fix destination path computation so the target differs from the source
  3. Normalize both paths before planning and drop identity moves

Example fix

// before
service.preflight_moves(moves)
// after
moves = [m for m in moves if m.old_path != m.new_path]
service.preflight_moves(moves)
Defensive patterns

Strategy: validation

Validate before calling

identical = [m for m in moves if m.old_path == m.new_path]
if identical:
    raise ValueError(f"identity moves: {[m.image_name for m in identical]}")
service.preflight_moves(moves)

Type guard

def is_real_move(move: PlannedImageMove) -> bool:
    return move.old_path != move.new_path

Try / catch

try:
    service.preflight_moves(moves)
except ValueError as e:
    if "identical" in str(e):
        moves = [m for m in moves if m.old_path != m.new_path]
        service.preflight_moves(moves)
    else:
        raise

Prevention

When it happens

Trigger: Computing a destination subfolder that resolves to the image's current location (e.g. moving to the same folder with the same name); passing identical old/new paths by mistake when constructing PlannedImageMove; path normalization differences resolved to the same path.

Common situations: UI allowing the user to select the image's current folder as the target; scripts that build target paths with empty suffix components; symlinks or relative paths that resolve identically.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/4ad47ddaa3ab78f2. Report an issue: GitHub.

Appendix: source

Thrown at invokeai/app/services/image_moves/image_moves_default.py:437

                    str(move.old_thumbnail_path),
                    str(move.new_thumbnail_path),
                    message,
                ),
            )
            return job_id

    def preflight_moves(self, moves: Sequence[PlannedImageMove]) -> None:
        destinations: set[Path] = set()
        thumbnail_destinations: set[Path] = set()
        for move in moves:
            if not move.old_path.exists():
                if not move.is_intermediate:
                    raise FileNotFoundError(f"Source image does not exist: {move.old_path}")
                continue
            if move.new_path.exists():
                raise FileExistsError(f"Destination image already exists: {move.new_path}")
            if move.old_path == move.new_path:
                raise ValueError(f"Old and new paths are identical for {move.image_name}")
            if move.new_path in destinations:
                raise ValueError(f"Duplicate destination path: {move.new_path}")
            destinations.add(move.new_path)
            if move.new_thumbnail_path in thumbnail_destinations:
                raise ValueError(f"Duplicate destination thumbnail path: {move.new_thumbnail_path}")
            thumbnail_destinations.add(move.new_thumbnail_path)
            if self._has_active_job_for_image(move.image_name):
                raise ValueError(f"Image {move.image_name} already has an active image move job")
            self._assert_same_filesystem(move.old_path, move.new_path)
            if move.old_thumbnail_path.exists():
                if move.new_thumbnail_path.exists():
                    raise FileExistsError(f"Destination thumbnail already exists: {move.new_thumbnail_path}")
                self._assert_same_filesystem(move.old_thumbnail_path, move.new_thumbnail_path)

    def _record_missing_source_errors(self, moves: Sequence[PlannedImageMove]) -> tuple[list[PlannedImageMove], int]:
        remaining_moves: list[PlannedImageMove] = []
        errors = 0
        for move in moves:

View on GitHub (pinned to 0b6a024f2f)