agalwood/Motrix · error · RangeError

Completed must end in completed

Error message

Completed must end in completed

What it means

For `TaskHistoryEventKind.Completed`, `toStatus` must be exactly `TaskStatus.Completed`. Throws this `RangeError` otherwise — a Completed event that lands anywhere else (e.g. `seeding`, `error`) is a logical contradiction. A BT task that finishes downloading but keeps seeding should use `StageChanged`, not `Completed`.

Source

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

      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:
      break
  }
}

export function normalizeTransferSamples(

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Reserve Completed for `toStatus === TaskStatus.Completed`.
  2. Use StageChanged for the download->seeding transition.
  3. Use Failed for terminal errors.

Example fix

// before
emit({ kind: TaskHistoryEventKind.Completed, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Seeding })
// after
emit({ kind: TaskHistoryEventKind.StageChanged, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Seeding })
Defensive patterns

Strategy: validation

Validate before calling

if (newStatus === TaskStatus.Completed) emit({ kind: TaskHistoryEventKind.Completed, /* ... */ })
else if (newStatus === TaskStatus.Error) emit({ kind: TaskHistoryEventKind.Failed, /* ... */ })
else emit({ kind: TaskHistoryEventKind.StageChanged, /* ... */ })

Prevention

When it happens

Trigger: Emitting Completed when the task transitions to `seeding` (BT post-download) or `error`; usually because the producer fired Completed on engine `complete` regardless of the resolved target.

Common situations: BT workflows where 'download finished' means 'now seeding'; engine signals that map to error rather than completion; tests reusing Completed for any terminal transition.

Related errors


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