{"record":{"id":"a385ecee1ee1de89","repo":"remotion-dev/remotion","slug":"path-text-must-be-a-string","errorCode":null,"errorMessage":"${path}.text must be a string.","messagePattern":"(.+?)\\.text must be a string\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/studio/src/components/parse-caption-file.ts","lineNumber":43,"sourceCode":"\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('Expected a Remotion Caption[] JSON array.');\n\t}\n\n\tlet previousStart: number | null = null;\n\tfor (const [index, caption] of parsed.entries()) {\n\t\tconst path = `captions[${index}]`;\n\t\tif (!isObject(caption)) {\n\t\t\tthrow new Error(`${path} must be an object.`);\n\t\t}\n\n\t\tif (typeof caption.text !== 'string') {\n\t\t\tthrow new Error(`${path}.text must be a string.`);\n\t\t}\n\n\t\tif (!isFiniteNumber(caption.startMs) || caption.startMs < 0) {\n\t\t\tthrow new Error(`${path}.startMs must be a finite, non-negative number.`);\n\t\t}\n\n\t\tif (!isFiniteNumber(caption.endMs) || caption.endMs < 0) {\n\t\t\tthrow new Error(`${path}.endMs must be a finite, non-negative number.`);\n\t\t}\n\n\t\tif (caption.endMs < caption.startMs) {\n\t\t\tthrow new Error(`${path}.endMs must not be earlier than startMs.`);\n\t\t}\n\n\t\tif (caption.timestampMs !== null && !isFiniteNumber(caption.timestampMs)) {\n\t\t\tthrow new Error(`${path}.timestampMs must be a finite number or null.`);\n\t\t}\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/studio/src/components/parse-caption-file.ts#L25-L61","documentation":"parseCaptionFile validates each entry of a parsed captions JSON array. When element at index N is an object but its text property is not a string, the error fires with a path like captions[N].text to pinpoint the offending field in the caption file.","triggerScenarios":"parseCaptionFile validates captions[i] and finds typeof caption.text !== 'string' — e.g. {\"startMs\":0,\"endMs\":1000} (missing text) or {\"text\": 123}.","commonSituations":"Field renamed to `body`/`content` by an exporting tool; missing text on auto-generated cues; JSON serialization turned text into a number.","solutions":["Add or rename the field to `text` with a string value","Quote the value: text must be a JSON string, not a number","Re-export with the Remotion caption schema"],"exampleFix":"// before\n{\"text\": 123, \"startMs\": 0, \"endMs\": 1000}\n// after\n{\"text\": \"123\", \"startMs\": 0, \"endMs\": 1000}","handlingStrategy":"validation","validationCode":"for (const [i, c] of parsed.entries()) {\n  if (typeof (c as any).text !== 'string') {\n    throw new Error(`captions[${i}].text must be a string`);\n  }\n}","typeGuard":"const hasText = (c: Record<string, unknown>): c is {text: string} & Record<string, unknown> =>\n  typeof c.text === 'string';","tryCatchPattern":"try {\n  const captions = parseCaptionFile({fileName, contents});\n} catch (e) {\n  // retry after normalizing the flagged field\n}","preventionTips":["Ensure exporting tools emit `text` as a string field","Never rename text to body/content without converting","Run the file through a Caption[] type check before import"],"tags":["json","validation","captions"],"backgroundTag":"missing-required-argument","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-09-09T13:27:51.281Z","contentChangedAt":"2026-09-09T13:27:51.281Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}