invoke-ai/InvokeAI · error

Image move job {job_id} has no items

Error message

Image move job {job_id} has no items

What it means

complete_partial_filesystem_moves requires the job to have non-terminal items to complete. If no such items exist, it re-checks including terminal items: if any exist the job is already done and it returns silently, otherwise the job truly has no items at all and it raises RuntimeError. This catches DB inconsistency (a job row with zero item rows).

Source

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

                continue
            message = f"Source image does not exist: {move.old_path}"
            self.create_error_move_job(move, message)
            self._logger.error(message)
            errors += 1
        return remaining_moves, errors

    def perform_filesystem_moves(self, job_id: int) -> None:
        self._set_job_state(job_id, "moving")
        self.complete_partial_filesystem_moves(job_id)
        self.cleanup_empty_source_dirs(job_id)
        self._set_job_state(job_id, "moved")

    def complete_partial_filesystem_moves(self, job_id: int) -> None:
        items = self._get_items(job_id, include_terminal=False)
        if not items:
            if self._get_items(job_id):
                return
            raise RuntimeError(f"Image move job {job_id} has no items")
        for item in items:
            try:
                self._complete_partial_filesystem_move(job_id, item)
            except Exception as e:
                if not self._is_unrecoverable_error(e):
                    raise
                self._reconcile_destination_subfolder(item)
                self.mark_item_unrecoverable(job_id, item.image_name, f"{item.image_name}: {e}")
                self._logger.error("Image move skipped unrecoverable item %s: %s", item.image_name, e)

    def _complete_partial_filesystem_move(self, job_id: int, item: PlannedImageMove) -> None:
        old_path = self.image_files.get_path(item.image_name, image_subfolder=item.old_subfolder)
        new_path = self.image_files.get_path(item.image_name, image_subfolder=item.new_subfolder)
        old_thumbnail_path = self.image_files.get_path(
            item.image_name, thumbnail=True, image_subfolder=item.old_subfolder
        )
        new_thumbnail_path = self.image_files.get_path(
            item.image_name, thumbnail=True, image_subfolder=item.new_subfolder

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Verify the job_id is correct and that item rows exist in image_subfolder_move_items for it
  2. If the job is genuinely empty/corrupt, mark it 'error' (or delete it) and create a fresh job
  3. Rely on the silent-return path: if all items are terminal, nothing needs completing
  4. Re-run the planning flow (move_all_images) to rebuild a valid job

Example fix

// before
service.complete_partial_filesystem_moves(job_id)  # RuntimeError if no items
// after
items = service.get_items(job_id)  # or query DB
if not items:
    mark_job_error(job_id)
else:
    service.complete_partial_filesystem_moves(job_id)
Defensive patterns

Strategy: try-catch

Validate before calling

items = get_items(job_id)  # any state
if not items:
    raise RuntimeError(f"job {job_id} has no items; re-plan or mark it errored")
service.complete_partial_filesystem_moves(job_id)

Type guard

def job_has_items(items: list[MoveItem]) -> bool:
    return len(items) > 0

Try / catch

try:
    service.complete_partial_filesystem_moves(job_id)
except RuntimeError as e:
    if "has no items" in str(e):
        mark_job_errored(job_id)
        job_id = replan_and_create_job()
    else:
        raise

Prevention

When it happens

Trigger: Calling complete_partial_filesystem_moves(job_id) for a job_id that was created but whose item inserts failed/were rolled back; passing a nonexistent or wrong job_id; startup recovery encountering a corrupted job row.

Common situations: Manual DB edits deleting item rows; crash between INSERT of the job and its items despite the transaction; application code caching/deriving a job_id incorrectly after re-creation.

Related errors


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