{"record":{"id":"d7cf7709b7e6d77b","repo":"agalwood/Motrix","slug":"resumed-must-transition-from-paused-to-active","errorCode":null,"errorMessage":"Resumed must transition from paused to active","messagePattern":"Resumed must transition from paused to active","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":226,"sourceCode":"      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      }\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:","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L208-L244","documentation":"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.","triggerScenarios":"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.","commonSituations":"Engine reporting `running` after the persisted status is already active; bridge code conflating resume with start; tests with mismatched transitions.","solutions":["Only emit Resumed when the persisted status is `paused` and the new status is active.","Use Started (not Resumed) when transitioning from a non-paused origin.","Drop no-op resumes."],"exampleFix":"// before\nemit({ kind: TaskHistoryEventKind.Resumed, fromStatus: TaskStatus.Downloading, toStatus: TaskStatus.Downloading })\n// after\nif (currentStatus === TaskStatus.Paused && ACTIVE.has(newStatus)) {\n  emit({ kind: TaskHistoryEventKind.Resumed, fromStatus: TaskStatus.Paused, toStatus: newStatus })\n}","handlingStrategy":"validation","validationCode":"const ACTIVE = new Set([TaskStatus.FetchingMetadata, TaskStatus.Downloading, TaskStatus.Finalizing, TaskStatus.Seeding])\nfunction canResume(from: TaskStatus | null, to: TaskStatus): boolean {\n  return from === TaskStatus.Paused && ACTIVE.has(to)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Emit Resumed only when the persisted status is Paused and target is active.","Distinguish Start (from null/queued) from Resume (from Paused)."],"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"}