invoke-ai/InvokeAI · error · ImageMoveJobAlreadyRunning

An image move job is already active

Error message

An image move job is already active

What it means

Beyond the in-process future check, _start_background_operation consults _get_active_job_id (persisted/journaled active job). If an active job id exists and the requested operation is not 'recovery', it raises ImageMoveJobAlreadyRunning('An image move job is already active') — recovery is deliberately allowed to take over from a crashed run.

Source

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

        if session_queue is None:
            return
        queue_status = session_queue.get_queue_status(DEFAULT_QUEUE_ID)
        if queue_status.pending > 0 or queue_status.in_progress > 0:
            raise ImageMoveQueueActive("Cannot start image move while queue work is active")

    def _start_background_operation(
        self,
        operation: ImageMoveBackgroundOperation,
        target,
        require_idle_queue: bool = False,
    ) -> ImageMoveBackgroundStatus:
        with self._future_lock:
            self._refresh_finished_future_locked()
            if self._future_operation is not None or (self._future is not None and not self._future.done()):
                raise ImageMoveJobAlreadyRunning("An image move job is already running")
            active_job_id = self._get_active_job_id()
            if operation != "recovery" and active_job_id is not None:
                raise ImageMoveJobAlreadyRunning("An image move job is already active")
            self._last_background_error = None
            self._future_operation = operation

        try:
            if require_idle_queue:
                self._assert_no_active_queue_work()
            future = self._executor.submit(self._run_background_operation, operation, target)
        except Exception:
            with self._future_lock:
                self._future_operation = None
            raise

        with self._future_lock:
            self._future = future
            return self._build_background_status_locked()

    def _run_background_operation(self, operation: ImageMoveBackgroundOperation, target) -> None:
        try:

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Run start_background_recovery first to complete/commit the interrupted job and clear the active id
  2. Clear or commit the stale journal entry via the maintenance/recovery API
  3. Restart cleanly and complete recovery before scheduling new move operations

Example fix

// before
service.start_background_move_all(target)  # raises: active job id exists
// after
service.start_background_recovery()  # finish interrupted job first
# then, once recovery completes:
service.start_background_move_all(target)
Defensive patterns

Strategy: try-catch

Validate before calling

def active_job_cleared(service) -> bool:
    st = service.get_maintenance_status()
    return not (st.is_maintenance_active or getattr(st, 'has_active_job', False))
if active_job_cleared(service):
    service.start_background_move_all(target)

Try / catch

from invokeai.app.services.image_moves.image_moves_common import ImageMoveJobAlreadyRunning
try:
    service.start_background_move_all(target)
except ImageMoveJobAlreadyRunning:
    service.start_background_recovery()  # clear stale active job, then retry
    service.start_background_move_all(target)

Prevention

When it happens

Trigger: Starting a new move while a journal/active-job record from a previous (possibly crashed) run exists; recovery interrupted by a non-recovery operation attempt with stale active job id.

Common situations: Server restarted mid-move leaving an uncommitted journal; previous job's active id never cleared due to a crash; attempting move_all before running recovery.

Related errors


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