agalwood/Motrix · error · RangeError
${label} must be a flat string record
Error message
${label} must be a flat string record What it means
`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`.
Source
Thrown at src/core/inspector-activity/validators.ts:149
function assertStatus(value: TaskStatus | null, label: string): void {
if (value !== null && !STATUS_VALUES.has(value)) {
throw new RangeError(`${label} is not a legal task status`)
}
}
function assertBoundedDetailParams(
value: Record<string, string> | null,
label: string,
maxJsonLength: number
): void {
if (value === null) return
if (
typeof value !== 'object' ||
Array.isArray(value) ||
Object.values(value).some((entry) => typeof entry !== 'string')
) {
throw new RangeError(`${label} must be a flat string record`)
}
const serialized = JSON.stringify(value)
if (serialized.length > maxJsonLength) {
throw new RangeError(
`${label} JSON must not exceed ${maxJsonLength} characters`
)
}
}
export function validateHistoryEventInput(input: TaskHistoryEventInput): void {
assertTaskId(input.taskId)
assertPositiveSafeInteger(input.eventOrdinal, 'eventOrdinal')
assertBoundedText(input.eventKey, 'eventKey', MAX_EVENT_KEY_LENGTH)
assertBoundedText(
input.runtimeGeneration,
'runtimeGeneration',
MAX_EVENT_KEY_LENGTH
)View on GitHub (pinned to 1a708ee577)
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>`.
Example fix
// before
event.errorDetailParams = rawEngineParams // {file: {name: 'x'}, size: 1024}
// after
event.errorDetailParams = Object.fromEntries(
Object.entries(rawEngineParams).map(([k, v]) => [k, typeof v === 'string' ? v : JSON.stringify(v)])
) Defensive patterns
Strategy: type-guard
Validate before calling
function flattenStringRecord(v: unknown): Record<string, string> | null {
if (v == null || typeof v !== 'object' || Array.isArray(v)) return null
const out: Record<string, string> = {}
for (const [k, val] of Object.entries(v as Record<string, unknown>))
out[k] = typeof val === 'string' ? val : JSON.stringify(val)
return out
} Type guard
function isFlatStringRecord(v: unknown): v is Record<string, string> {
return typeof v === 'object' && v !== null && !Array.isArray(v) &&
Object.values(v).every((x) => typeof x === 'string')
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- taskId must be a string
- taskId must contain between 1 and ${MAX_TASK_ID_LENGTH} char
- ${label} must be a positive safe integer
- ${label} must be a non-negative safe integer
- ${label} must be a non-negative bigint
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/ee03067bac7ebb45.
Report an issue: GitHub.