{"record":{"id":"2cde7f0d3f0bebdc","repo":"agalwood/Motrix","slug":"label-json-must-not-exceed-maxjsonlength-cha","errorCode":null,"errorMessage":"${label} JSON must not exceed ${maxJsonLength} characters","messagePattern":"(.+?) JSON must not exceed (.+?) characters","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/core/inspector-activity/validators.ts","lineNumber":153,"sourceCode":"  }\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  )\n  assertPositiveSafeInteger(input.occurredAt, 'occurredAt')\n  assertNonNegativeSafeInteger(input.occurredMonotonicMs, 'occurredMonotonicMs')\n  if (!EVENT_KIND_VALUES.has(input.kind)) {\n    throw new RangeError('kind is not a legal history event kind')","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/inspector-activity/validators.ts#L135-L171","documentation":"After the value passes the flat-string-record check, it is serialized with `JSON.stringify` and the resulting string must be at most `MAX_ERROR_DETAIL_PARAMS_JSON_LENGTH` (2048) characters. This bound matches the SQLite column width so the serialized blob fits. Throws this `RangeError` on overflow.","triggerScenarios":"An `errorDetailParams` object whose JSON form exceeds 2048 chars — many keys, long values, or a few long URLs. E.g. storing a stack trace, a full request body, or a list of peer addresses as a single param value.","commonSituations":"Forwarding a verbose engine error payload (full request URL, headers, peer list) into params; accumulating params across retries without trimming.","solutions":["Trim each param value to a per-key cap (e.g. 256 chars) before serialization.","Drop low-value keys and keep only those referenced by the i18n message template.","If the detail genuinely needs more space, persist it elsewhere and reference it by id in `errorDetailKey`.","Compute `JSON.stringify(...).length` at the producer and prune until under 2048."],"exampleFix":"// before\nevent.errorDetailParams = { stack: err.stack }  // multi-KB\n// after\nevent.errorDetailParams = err.stack ? { stack: err.stack.slice(0, 1024) } : null","handlingStrategy":"validation","validationCode":"const MAX = 2048\nfunction fitDetailParams(params: Record<string, string>): Record<string, string> | null {\n  const out = { ...params }\n  while (JSON.stringify(out).length > MAX) {\n    const longest = Object.keys(out).sort((a, b) => out[b].length - out[a].length)[0]\n    if (!longest) return null\n    out[longest] = out[longest].slice(0, Math.floor(out[longest].length / 2))\n  }\n  return out\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep param values short — they exist for i18n interpolation, not payload storage.","Trim each value at the producer before assembling the record.","Reserve `errorDetailKey` + an external store for large diagnostics."],"tags":["validation","range-error","json","size-limits"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}