{"record":{"id":"ee03067bac7ebb45","repo":"agalwood/Motrix","slug":"label-must-be-a-flat-string-record","errorCode":null,"errorMessage":"${label} must be a flat string record","messagePattern":"(.+?) must be a flat string record","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":149,"sourceCode":"\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) {\n    throw new RangeError(\n      `${label} JSON must not exceed ${maxJsonLength} characters`\n    )\n  }\n}\n\nexport function validateHistoryEventInput(input: TaskHistoryEventInput): void {\n  assertTaskId(input.taskId)\n  assertPositiveSafeInteger(input.eventOrdinal, 'eventOrdinal')\n  assertBoundedText(input.eventKey, 'eventKey', MAX_EVENT_KEY_LENGTH)\n  assertBoundedText(\n    input.runtimeGeneration,\n    'runtimeGeneration',\n    MAX_EVENT_KEY_LENGTH\n  )","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L131-L167","documentation":"`assertBoundedDetailParams` accepts `null` or a plain object whose every value is a string — `Record<string,string>`. It throws this `RangeError` if the value is not an object, is an array, or contains any non-string value. The shape mirrors i18n interpolation params (`{file: 'movie.mp4', host: 'example.com'}`). Used only for `errorDetailParams`.","triggerScenarios":"Passing `errorDetailParams` with nested objects (`{file: {name: '...'}}`), numbers (`{count: 5}`), arrays, or as a raw string instead of a record. Common at sites that forward an arbitrary engine error payload into the event without normalization.","commonSituations":"Engine error bodies whose params arrive as JSON with mixed types; copy-pasting a `Record<string, unknown>` into the field; tests that pass `{ size: 1024 }` directly.","solutions":["Stringify every param value before assignment: `Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, String(v)]))`.","Filter out non-scalar values or stringify nested objects with `JSON.stringify`.","Drop the field entirely (`null`) when params are not primitive strings.","Validate at the IPC edge with a schema that enforces `Record<string,string>`."],"exampleFix":"// before\nevent.errorDetailParams = rawEngineParams  // {file: {name: 'x'}, size: 1024}\n// after\nevent.errorDetailParams = Object.fromEntries(\n  Object.entries(rawEngineParams).map(([k, v]) => [k, typeof v === 'string' ? v : JSON.stringify(v)])\n)","handlingStrategy":"type-guard","validationCode":"function flattenStringRecord(v: unknown): Record<string, string> | null {\n  if (v == null || typeof v !== 'object' || Array.isArray(v)) return null\n  const out: Record<string, string> = {}\n  for (const [k, val] of Object.entries(v as Record<string, unknown>))\n    out[k] = typeof val === 'string' ? val : JSON.stringify(val)\n  return out\n}","typeGuard":"function isFlatStringRecord(v: unknown): v is Record<string, string> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    Object.values(v).every((x) => typeof x === 'string')\n}","tryCatchPattern":null,"preventionTips":["Coerce params to strings at the engine/IPC boundary.","Keep error-detail params a flat `Record<string,string>` from creation — never store structured data there.","Reject nested payloads up front; serialize them if you must keep them."],"tags":["validation","range-error","objects","i18n"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}