remotion-dev/remotion · error
Expected the JSX name attribute to be a string
Error message
Expected the JSX name attribute to be a string
What it means
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).
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
Example fix
// before
<Sequence name={title}>...</Sequence>
// after
<Sequence name="Title">...</Sequence> Defensive patterns
Strategy: validation
Validate before calling
const nameAttr = element.openingElement.attributes.find((a) => a.type === 'JSXAttribute' && a.name.name === 'name');
if (nameAttr && !(nameAttr.value?.type === 'StringLiteral' || (nameAttr.value?.type === 'JSXExpressionContainer' && nameAttr.value.expression.type === 'StringLiteral'))) {
throw new Error('name prop must be a static string before duplicating');
} Type guard
const isStringNameAttr = (attr: JSXAttribute | null | undefined): attr is JSXAttribute & {value: StringLiteral} =>
!!attr && (attr.value?.type === 'StringLiteral' || (attr.value?.type === 'JSXExpressionContainer' && attr.value.expression.type === 'StringLiteral')); Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cannot duplicate JSX element with no parent
- Cannot split a JSX sequence without a source location
- Cannot split audio from a JSX element with no parent
- Cannot split JSX sequence with no parent
- Could not find a root JSX element
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/808275803fa27827.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio-codemods/src/duplicate-jsx-node.ts:204
const end = recastLocToOffset(input, element.loc.end);
let source = input.slice(start, end);
const nameAttribute = element.openingElement.attributes.find(
(attribute) =>
attribute.type === 'JSXAttribute' &&
attribute.name.type === 'JSXIdentifier' &&
attribute.name.name === 'name' &&
(attribute.value?.type === 'StringLiteral' ||
(attribute.value?.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'StringLiteral')),
);
if (nameAttribute?.type === 'JSXAttribute') {
const value =
nameAttribute.value?.type === 'JSXExpressionContainer'
? nameAttribute.value.expression
: nameAttribute.value;
if (value?.type !== 'StringLiteral') {
throw new Error('Expected the JSX name attribute to be a string');
}
const edit = getJsxStringAttributeValueSourceEdit({
attribute: nameAttribute,
input,
newValue: `${value.value}-copy`,
});
source =
source.slice(0, edit.start - start) +
edit.replacement +
source.slice(edit.end - start);
}
const originalIndent = getLineIndent({input, offset: start});
return source
.split(/\r?\n/)
.map((line, index) => {
if (index === 0) {View on GitHub (pinned to 46a3a6bf13)