remotion-dev/remotion · error · Error
Unexpected JSX attribute character
Error message
Unexpected JSX attribute character: ${character} What it means
escapeJsxStringAttribute escapes special characters in JSX string attribute values using a per-character switch. Any character outside the explicitly handled set (quotes, tab, CR, LF, etc.) hits the default branch and throws this error naming the offending character. It is a strict allowlist rather than a generic escaper.
Solutions
- Sanitize the attribute string before calling the codemod: strip or replace control/unusual characters.
- Replace the offending character with a safe equivalent (e.g. escape it yourself with a unicode escape like \uXXXX inside the attribute value).
- Trim/normalize text inputs (e.g. replace non-breaking spaces, remove \u0000-\u001F except tab/newline) before insertion.
- If legitimate, extend the escaper's handled-character set in print-jsx.ts to cover the character.
Example fix
// before -> throws on control char value = 'title\u0007text'; // after value = 'title\u0007text'.replace(/[\u0000-\u001F\u007F]/g, (c) => c === '\t' || c === '\n' ? c : ' ');
Defensive patterns
Strategy: validation
Validate before calling
const sanitizeForJsxAttr = (s: string): string =>
s.replace(/[\u0000-\u001F\u007F\u00A0]/g, (c) =>
(c === '\t' || c === '\n' || c === '\r') ? c : ' ');
insertJsxElement({attributes: {title: sanitizeForJsxAttr(userTitle)}, ...}); Type guard
const isJsxAttrSafe = (s: string): boolean => [...s].every((c) => c === '\t' || c === '\n' || c === '\r' || (c >= ' ' && c !== '\u007F'));
Try / catch
try {
insertJsxElement(opts);
} catch (e) {
const m = /Unexpected JSX attribute character: (.)/.exec(e.message);
if (m) {
console.warn(`Stripping unsupported character ${JSON.stringify(m[1])}`);
insertJsxElement({...opts, attributes: sanitize(opts.attributes)});
} else throw e;
} Prevention
- Sanitize all user-derived or copied text before using it in JSX attributes
- Normalize whitespace (non-breaking spaces, control codes) at input boundaries
- Keep attribute values plain text; escape Unicode yourself with \uXXXX if needed
When it happens
Trigger: Calling escapeJsxStringAttribute (via the attributes printing path) with an attribute value containing a character not in the handled escape set — e.g. unusual control characters, non-breaking spaces treated as control chars, or unexpected Unicode in the allowed-control-character range.
Common situations: Attribute values sourced from user input or copied text containing exotic characters; capture/serialization pipelines that embed raw control codes into attribute strings; values pasted from binary or rich-text sources.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot duplicate a JSX element without a source location
- Cannot duplicate JSX element with no parent
- Cannot split a JSX sequence without a source location
- Cannot split JSX sequence with no parent
- Cannot update effect without a JSX source location
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/7c42a4483141ae06.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio-codemods/src/print-jsx.ts:34
return '&';
case '"':
return '"';
case '<':
return '<';
case '>':
return '>';
case '{':
return '{';
case '}':
return '}';
case '\t':
return '	';
case '\r':
return ' ';
case '\n':
return ' ';
default:
throw new Error(`Unexpected JSX attribute character: ${character}`);
}
});
};
export const indentInsertedJsx = ({
indent,
insertion,
}: {
indent: string;
insertion: string;
}) => {
return insertion
.split(/\r?\n/)
.map((line) => `${indent}${line}`)
.join(insertion.includes('\r\n') ? '\r\n' : '\n');
};
export const printInsertedJsx = ({View on GitHub (pinned to 46a3a6bf13)