{"record":{"id":"808275803fa27827","repo":"remotion-dev/remotion","slug":"expected-the-jsx-name-attribute-to-be-a-string","errorCode":null,"errorMessage":"Expected the JSX name attribute to be a string","messagePattern":"Expected the JSX name attribute to be a string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/studio-codemods/src/duplicate-jsx-node.ts","lineNumber":204,"sourceCode":"\tconst end = recastLocToOffset(input, element.loc.end);\n\tlet source = input.slice(start, end);\n\tconst nameAttribute = element.openingElement.attributes.find(\n\t\t(attribute) =>\n\t\t\tattribute.type === 'JSXAttribute' &&\n\t\t\tattribute.name.type === 'JSXIdentifier' &&\n\t\t\tattribute.name.name === 'name' &&\n\t\t\t(attribute.value?.type === 'StringLiteral' ||\n\t\t\t\t(attribute.value?.type === 'JSXExpressionContainer' &&\n\t\t\t\t\tattribute.value.expression.type === 'StringLiteral')),\n\t);\n\n\tif (nameAttribute?.type === 'JSXAttribute') {\n\t\tconst value =\n\t\t\tnameAttribute.value?.type === 'JSXExpressionContainer'\n\t\t\t\t? nameAttribute.value.expression\n\t\t\t\t: nameAttribute.value;\n\t\tif (value?.type !== 'StringLiteral') {\n\t\t\tthrow new Error('Expected the JSX name attribute to be a string');\n\t\t}\n\n\t\tconst edit = getJsxStringAttributeValueSourceEdit({\n\t\t\tattribute: nameAttribute,\n\t\t\tinput,\n\t\t\tnewValue: `${value.value}-copy`,\n\t\t});\n\t\tsource =\n\t\t\tsource.slice(0, edit.start - start) +\n\t\t\tedit.replacement +\n\t\t\tsource.slice(edit.end - start);\n\t}\n\n\tconst originalIndent = getLineIndent({input, offset: start});\n\treturn source\n\t\t.split(/\\r?\\n/)\n\t\t.map((line, index) => {\n\t\t\tif (index === 0) {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/studio-codemods/src/duplicate-jsx-node.ts#L186-L222","documentation":"duplicateJsxNodes (Remotion Studio codemod that duplicates a JSX element in a composition's source) rewrites a Sequence's `name` prop to `<old>-copy` when duplicating. When a `name` attribute exists, the library expects its value to be a string literal (optionally wrapped in `{}`); anything else cannot be uniquely renamed, so it throws. In practice the attribute filter already requires a StringLiteral, so this is an internal invariant that fires only on unexpected AST shapes (e.g. a JSXExpressionContainer with an empty or non-string expression).","triggerScenarios":"Calling duplicateJsxNodes / getDuplicatedJsxSource on source where an element has a `name` attribute whose value is a JSXExpressionContainer wrapping something other than a string literal (e.g. `name={getTitle()}`), or a malformed/container-valued name attribute that slipped past the filter.","commonSituations":"Duplicating a Sequence whose name prop is computed dynamically (`name={someVar}`) instead of a static string; programmatically generated JSX with container-wrapped name values; older hand-written compositions where name was dynamic.","solutions":["Change the `name` prop to a static string literal, e.g. name=\"MySeq\", before duplicating","Rename the attribute so it is not exactly `name` if a dynamic value is intentional","Parse your input and assert the name attribute value type before calling the API","Report the AST shape if you believe a valid string name triggered this — it indicates an invariant bug"],"exampleFix":"// before\n<Sequence name={title}>...</Sequence>\n// after\n<Sequence name=\"Title\">...</Sequence>","handlingStrategy":"validation","validationCode":"const nameAttr = element.openingElement.attributes.find((a) => a.type === 'JSXAttribute' && a.name.name === 'name');\nif (nameAttr && !(nameAttr.value?.type === 'StringLiteral' || (nameAttr.value?.type === 'JSXExpressionContainer' && nameAttr.value.expression.type === 'StringLiteral'))) {\n  throw new Error('name prop must be a static string before duplicating');\n}","typeGuard":"const isStringNameAttr = (attr: JSXAttribute | null | undefined): attr is JSXAttribute & {value: StringLiteral} =>\n  !!attr && (attr.value?.type === 'StringLiteral' || (attr.value?.type === 'JSXExpressionContainer' && attr.value.expression.type === 'StringLiteral'));","tryCatchPattern":"try { await duplicateJsxNodes({input, nodePaths}); } catch (e) { if ((e as Error).message.includes('name attribute to be a string')) { /* fall back: duplicate without rename or ask user to make name static */ } else throw e; }","preventionTips":["Keep Sequence name props as static string literals","Do not compute name={expr} on elements you intend to duplicate via codemods","Parse and lint for dynamic name props before running codemods"],"tags":["ast","jsx","codemod","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"46a3a6bf13ca367376ff18bbb06dae2e87e0d068","analyzedAt":"2026-09-18T00:12:20.256Z","contentChangedAt":"2026-09-18T00:12:20.256Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}