{"record":{"id":"9fd7f8aae5b36acf","repo":"agalwood/Motrix","slug":"added-must-not-have-a-fromstatus","errorCode":null,"errorMessage":"Added must not have a fromStatus","messagePattern":"Added must not have a fromStatus","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":204,"sourceCode":"    input.errorMessage,\n    'errorMessage',\n    MAX_ERROR_MESSAGE_LENGTH\n  )\n  assertBoundedText(\n    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 (","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L186-L222","documentation":"The first switch arm of the state machine: for `TaskHistoryEventKind.Added` (task creation), `fromStatus` must be `null` because there is no previous status. Throws this `RangeError` if `fromStatus` is set. This prevents confusion between an Add event and a transition.","triggerScenarios":"Constructing an Added event but populating `fromStatus` with `TaskStatus.Queued` (or anything else) — typically copy-pasted from a transition template or set by a generic builder that always fills both statuses.","commonSituations":"Generic event builders that default `fromStatus` to the current persisted status; replaying a task's first event with the previous row's leftover state; tests reusing a transition fixture for an Added case.","solutions":["Force `fromStatus = null` when kind is `Added`.","Split the builder: a dedicated `buildAddedEvent(...)` that does not accept a fromStatus.","Validate at the producer with `assert(kind !== Added || fromStatus === null)`."],"exampleFix":"// before\n{ kind: TaskHistoryEventKind.Added, fromStatus: TaskStatus.Queued, toStatus: TaskStatus.Queued }\n// after\n{ kind: TaskHistoryEventKind.Added, fromStatus: null, toStatus: TaskStatus.Queued }","handlingStrategy":"validation","validationCode":"if (input.kind === TaskHistoryEventKind.Added) input.fromStatus = null","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use dedicated builders per event kind rather than one generic factory.","Treat `Added` as semantically distinct from a transition — no fromStatus."],"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"}