{"record":{"id":"18589584670ef033","repo":"agalwood/Motrix","slug":"started-must-enter-an-active-status","errorCode":null,"errorMessage":"Started must enter an active status","messagePattern":"Started must enter an active status","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":209,"sourceCode":"    input.errorDetailKey,\n    'errorDetailKey',\n    MAX_ERROR_DETAIL_KEY_LENGTH\n  )\n  assertBoundedDetailParams(\n    input.errorDetailParams,\n    'errorDetailParams',\n    MAX_ERROR_DETAIL_PARAMS_JSON_LENGTH\n  )\n\n  switch (input.kind) {\n    case TaskHistoryEventKind.Added:\n      if (input.fromStatus !== null) {\n        throw new RangeError('Added must not have a fromStatus')\n      }\n      break\n    case TaskHistoryEventKind.Started:\n      if (!ACTIVE_RESUME_STATUSES.has(input.toStatus)) {\n        throw new RangeError('Started must enter an active status')\n      }\n      break\n    case TaskHistoryEventKind.Paused:\n      if (\n        input.fromStatus === null ||\n        !ACTIVE_RESUME_STATUSES.has(input.fromStatus) ||\n        input.toStatus !== TaskStatus.Paused\n      ) {\n        throw new RangeError('Paused must transition from active to paused')\n      }\n      break\n    case TaskHistoryEventKind.Resumed:\n      if (\n        input.fromStatus !== TaskStatus.Paused ||\n        !ACTIVE_RESUME_STATUSES.has(input.toStatus)\n      ) {\n        throw new RangeError('Resumed must transition from paused to active')\n      }","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L191-L227","documentation":"For `TaskHistoryEventKind.Started`, `toStatus` must be one of the active-resume statuses: `fetching_metadata`, `downloading`, `finalizing`, `seeding`. Throws this `RangeError` otherwise — a Started event that lands in `paused`, `completed`, `error`, `queued`, etc., is a logical contradiction.","triggerScenarios":"Submitting a Started event with `toStatus: TaskStatus.Paused` or `TaskStatus.Queued` — usually because the producer emitted Started on every state change instead of only on actual start, or because a status-translation table mapped the engine's `running` to the wrong target.","commonSituations":"Engine-bridge code that fires Started whenever aria2 reports activity, regardless of the resolved target status; tests with mismatched fixtures; off-by-one in status mapping.","solutions":["Only emit Started when the resolved target status is in `ACTIVE_RESUME_STATUSES`.","For other transitions (Paused, Completed, Error) emit the matching kind (`Paused`, `Completed`, `Failed`).","Re-check the engine-to-TaskStatus mapping for `running`/`active`."],"exampleFix":"// before\nemit({ kind: TaskHistoryEventKind.Started, fromStatus: null, toStatus: TaskStatus.Paused })\n// after\nemit({ kind: TaskHistoryEventKind.Paused, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Paused })","handlingStrategy":"validation","validationCode":"const ACTIVE = new Set([TaskStatus.FetchingMetadata, TaskStatus.Downloading, TaskStatus.Finalizing, TaskStatus.Seeding])\nif (input.kind === TaskHistoryEventKind.Started && !ACTIVE.has(input.toStatus))\n  throw new Error('Started requires an active toStatus; pick another kind')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Emit Started only when the target status is active.","Map each engine transition to the correct `TaskHistoryEventKind` rather than reusing Started."],"tags":["validation","range-error","state-machine","events"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}