agalwood/Motrix · error · RangeError

Resumed must transition from paused to active

Error message

Resumed must transition from paused to active

What it means

For `TaskHistoryEventKind.Resumed`, `fromStatus` must be exactly `TaskStatus.Paused` and `toStatus` must be in `ACTIVE_RESUME_STATUSES`. Throws this `RangeError` otherwise — resuming is only meaningful from a paused state, and resume must land the task back in an active status.

Source

Thrown at src/core/inspector-activity/validators.ts:226

      if (!ACTIVE_RESUME_STATUSES.has(input.toStatus)) {
        throw new RangeError('Started must enter an active status')
      }
      break
    case TaskHistoryEventKind.Paused:
      if (
        input.fromStatus === null ||
        !ACTIVE_RESUME_STATUSES.has(input.fromStatus) ||
        input.toStatus !== TaskStatus.Paused
      ) {
        throw new RangeError('Paused must transition from active to paused')
      }
      break
    case TaskHistoryEventKind.Resumed:
      if (
        input.fromStatus !== TaskStatus.Paused ||
        !ACTIVE_RESUME_STATUSES.has(input.toStatus)
      ) {
        throw new RangeError('Resumed must transition from paused to active')
      }
      break
    case TaskHistoryEventKind.Completed:
      if (input.toStatus !== TaskStatus.Completed) {
        throw new RangeError('Completed must end in completed')
      }
      break
    case TaskHistoryEventKind.Failed:
      if (input.toStatus !== TaskStatus.Error) {
        throw new RangeError('Failed must end in error')
      }
      break
    case TaskHistoryEventKind.ObservedState:
      if (input.accuracy !== TaskHistoryAccuracy.Recovered) {
        throw new RangeError('Observed state must be recovered')
      }
      break
    case TaskHistoryEventKind.StageChanged:

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Only emit Resumed when the persisted status is `paused` and the new status is active.
  2. Use Started (not Resumed) when transitioning from a non-paused origin.
  3. Drop no-op resumes.

Example fix

// before
emit({ kind: TaskHistoryEventKind.Resumed, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Downloading })
// after
if (currentStatus === TaskStatus.Paused && ACTIVE.has(newStatus)) {
  emit({ kind: TaskHistoryEventKind.Resumed, fromStatus: TaskStatus.Paused, toStatus: newStatus })
}
Defensive patterns

Strategy: validation

Validate before calling

const ACTIVE = new Set([TaskStatus.FetchingMetadata, TaskStatus.Downloading, TaskStatus.Finalizing, TaskStatus.Seeding])
function canResume(from: TaskStatus | null, to: TaskStatus): boolean {
  return from === TaskStatus.Paused && ACTIVE.has(to)
}

Prevention

When it happens

Trigger: A Resumed event whose `fromStatus` is `downloading` (resume from active — should be a no-op or a different kind), or whose `toStatus` is `paused`/`completed`/`error`. Usually the producer fired Resumed on every engine `running` notification without checking the prior status.

Common situations: Engine reporting `running` after the persisted status is already active; bridge code conflating resume with start; tests with mismatched transitions.

Related errors


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