agalwood/Motrix · error · AppError

TaskRemoveNotAvailableDuringFinalize

TaskRemoveNotAvailableDuringFinalize

Error message

Cannot remove task while it is being finalized

What it means

Thrown by `removeTaskUnderMutation` (remove-task.ts:89) when the task being removed has `status === TaskStatus.Finalizing`. Removal during the narrow finalize window is forbidden because finalize holds the stale original GID and a concurrent delete could leave a replacement GID live in aria2 after the parent row is gone.

Source

Thrown at src/core/task/actions/remove-task.ts:89

  await deps.runTaskMutation([taskId], remove)
}

/**
 * The task snapshot and every external side effect must share one admission
 * lock. A late lock around only the durable delete lets reAddTask create and
 * publish a replacement GID while removal still holds the stale original GID,
 * leaving the replacement live in aria2 after the parent row is deleted.
 */
async function removeTaskUnderMutation(
  taskId: string,
  options: RemoveTaskOptions,
  deps: RemoveTaskDeps
): Promise<void> {
  const task = getTaskOrWarn(deps, taskId, 'removeTask')
  if (!task) return

  if (task.status === TaskStatus.Finalizing) {
    throw new AppError(
      ErrorCode.TaskRemoveNotAvailableDuringFinalize,
      'Cannot remove task while it is being finalized'
    )
  }

  // Both removal paths delete the durable parent row under one
  // exclusive-persistence window, with the Inspector Activity deletion
  // barrier (when wired) installed before the row disappears. The barrier
  // ordering is the delicate part — keep it in exactly one place.
  const deleteParentBarrier = (publish: () => void): Promise<void> =>
    deps.taskPersistence.runExclusivePersistence(async () => {
      if (deps.deleteParentTasks) {
        await deps.deleteParentTasks([taskId], publish)
      } else {
        publish()
      }
    })

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Retry the remove shortly after the Finalizing window closes (finalize is bounded and short).
  2. Have the UI hide/disable the remove action while task.status === Finalizing.
  3. If removal must be forced, wait for the finalize completion event then issue remove.
  4. In batch clear logic, skip Finalizing tasks and re-sweep them on the next pass.

Example fix

// before
if (task.status === TaskStatus.Finalizing) {
  throw new AppError(ErrorCode.TaskRemoveNotAvailableDuringFinalize, 'Cannot remove task while it is being finalized')
}

// after — caller retries with backoff
try {
  await removeTask(taskId, {}, deps)
} catch (e) {
  if (e instanceof AppError && e.code === ErrorCode.TaskRemoveNotAvailableDuringFinalize) {
    await once(statusBus, `task:${taskId}:status`)
    return removeTask(taskId, {}, deps)
  }
  throw e
}
Defensive patterns

Strategy: retry

Validate before calling

function canRemoveNow(task) { return task.status !== TaskStatus.Finalizing }

Type guard

function isRemoveDuringFinalize(e) { return e instanceof AppError && e.code === ErrorCode.TaskRemoveNotAvailableDuringFinalize }

Try / catch

try {
  await removeTask(taskId, {}, deps)
} catch (e) {
  if (e instanceof AppError && e.code === ErrorCode.TaskRemoveNotAvailableDuringFinalize) {
    await once(statusBus, `task:${taskId}:status`) // wait out the finalize window
    return removeTask(taskId, {}, deps)
  }
  throw e
}

Prevention

When it happens

Trigger: User or automation issues a remove-task call while finalize is mid-flight (between rename and metadata commit); a bulk 'clear all' races with an active finalize; a UI double-action where the user clicks remove right as a download completes.

Common situations: Race between a completion-triggered finalize and a user remove; scripted cleanup loops that don't account for the Finalizing window; retry-after-failure automation colliding with finalize.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/b20aee4e69b2c914. Report an issue: GitHub.