{"record":{"id":"064c9a52e9e47f81","repo":"agalwood/Motrix","slug":"label-is-not-a-legal-task-status","errorCode":null,"errorMessage":"${label} is not a legal task status","messagePattern":"(.+?) is not a legal task status","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":134,"sourceCode":"  value: string | null,\n  label: string,\n  maxLength: number\n): void {\n  if (value === null) return\n  if (\n    typeof value !== 'string' ||\n    value.length === 0 ||\n    value.length > maxLength\n  ) {\n    throw new RangeError(\n      `${label} must contain between 1 and ${maxLength} characters`\n    )\n  }\n}\n\nfunction assertStatus(value: TaskStatus | null, label: string): void {\n  if (value !== null && !STATUS_VALUES.has(value)) {\n    throw new RangeError(`${label} is not a legal task status`)\n  }\n}\n\nfunction assertBoundedDetailParams(\n  value: Record<string, string> | null,\n  label: string,\n  maxJsonLength: number\n): void {\n  if (value === null) return\n  if (\n    typeof value !== 'object' ||\n    Array.isArray(value) ||\n    Object.values(value).some((entry) => typeof entry !== 'string')\n  ) {\n    throw new RangeError(`${label} must be a flat string record`)\n  }\n  const serialized = JSON.stringify(value)\n  if (serialized.length > maxJsonLength) {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L116-L152","documentation":"`assertStatus` allows `null` (status not applicable) or any value present in the `TaskStatus` enum: `queued`, `fetching_metadata`, `metadata_ready`, `downloading`, `finalizing`, `seeding`, `paused`, `completed`, `error`, `removed`. It throws this `RangeError` when the value is a non-null string outside that set. This protects the SQLite `status` column's CHECK constraint and the downstream state-machine invariants. `label` is `fromStatus` or `toStatus`.","triggerScenarios":"Submitting a `TaskHistoryEventInput` whose `fromStatus`/`toStatus` carries a value outside the enum — e.g. an old serialized name (`'fetching'`), a foreign status (`'running'`), or a numeric status code that should have been translated. Surfaces when replaying history from an older schema or bridging an external engine's status vocabulary.","commonSituations":"Version drift: an event written under a previous release where a status had a different identifier; consuming a third-party engine's status enum directly; tests with hardcoded string literals that drift from the enum.","solutions":["Map the incoming status through a translation table whose keys cover every foreign value, falling through to a known default.","If the source is local, reference `TaskStatus.X` instead of a string literal so the compiler tracks renames.","Drop or quarantine events with unmappable statuses rather than passing them to the validator.","Re-run any migration that backfills old status identifiers."],"exampleFix":"// before\ninput.fromStatus = engineState  // 'running' — not in TaskStatus\n// after\nconst FROM_ENGINE: Record<string, TaskStatus> = {\n  running: TaskStatus.Downloading,\n  stopped: TaskStatus.Paused,\n}\ninput.fromStatus = FROM_ENGINE[engineState] ?? null","handlingStrategy":"type-guard","validationCode":"import { TaskStatus } from '@shared/types/task'\nconst STATUS_VALUES = new Set(Object.values(TaskStatus))\nfunction coerceStatus(v: unknown): TaskStatus | null {\n  return typeof v === 'string' && STATUS_VALUES.has(v) ? (v as TaskStatus) : null\n}","typeGuard":"import { TaskStatus } from '@shared/types/task'\nconst STATUS_VALUES = new Set(Object.values(TaskStatus))\nfunction isTaskStatus(v: unknown): v is TaskStatus {\n  return typeof v === 'string' && STATUS_VALUES.has(v)\n}","tryCatchPattern":null,"preventionTips":["Never hardcode status strings — import `TaskStatus` enum members.","Translate foreign statuses at the engine boundary, not at the validator.","Add a snapshot/golden test of the status vocabulary so renames surface in CI."],"tags":["validation","range-error","state-machine","enums"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}