{"record":{"id":"e5f91b1b40a9173d","repo":"agalwood/Motrix","slug":"failed-must-end-in-error","errorCode":null,"errorMessage":"Failed must end in error","messagePattern":"Failed must end in error","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":236,"sourceCode":"        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      }\n      break\n    case TaskHistoryEventKind.Completed:\n      if (input.toStatus !== TaskStatus.Completed) {\n        throw new RangeError('Completed must end in completed')\n      }\n      break\n    case TaskHistoryEventKind.Failed:\n      if (input.toStatus !== TaskStatus.Error) {\n        throw new RangeError('Failed must end in error')\n      }\n      break\n    case TaskHistoryEventKind.ObservedState:\n      if (input.accuracy !== TaskHistoryAccuracy.Recovered) {\n        throw new RangeError('Observed state must be recovered')\n      }\n      break\n    case TaskHistoryEventKind.StageChanged:\n      break\n  }\n}\n\nexport function normalizeTransferSamples(\n  samples: readonly TaskTransferSample[]\n): TaskTransferSample[] {\n  return samples.map((sample, index) => ({\n    t: assertPositiveSafeInteger(sample.t, `samples[${index}].t`),\n    down: normalizeSpeed(sample.down, `samples[${index}].down`),","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L218-L254","documentation":"For `TaskHistoryEventKind.Failed`, `toStatus` must be exactly `TaskStatus.Error`. Throws this `RangeError` otherwise — a failure event must land the task in the error state. Non-error terminal statuses belong to other kinds.","triggerScenarios":"Emitting Failed with `toStatus: TaskStatus.Completed` or `TaskStatus.Removed`; usually a producer firing Failed on any abort without checking whether the engine reported a clean finish or an error.","commonSituations":"Engine abort that the bridge maps to Removed rather than Error; tests reusing Failed for any unsuccessful outcome; status-translation bug mapping error to completed.","solutions":["Reserve Failed for `toStatus === TaskStatus.Error`.","Use the appropriate kind for other terminal transitions (Completed, StageChanged).","Translate the engine error code to `TaskStatus.Error` at the boundary."],"exampleFix":"// before\nemit({ kind: TaskHistoryEventKind.Failed, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Removed })\n// after\nemit({ kind: TaskHistoryEventKind.Failed, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Error })","handlingStrategy":"validation","validationCode":"if (engineError) emit({ kind: TaskHistoryEventKind.Failed, toStatus: TaskStatus.Error, /* ... */ })","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Map engine failures to `TaskStatus.Error` before emitting Failed.","Distinguish removal from failure — they are different terminal states."],"tags":["validation","range-error","state-machine","events"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}