emberjs/ember.js · error
Unexpected literal `${value}`
Error message
Unexpected literal `${value}` What it means
assertIllegalLiteral fires when a literal (string, number, boolean, null, undefined) appears in a template position where only expressions/paths are legal — typically as a callee/argument position in subexpressions or as a path in a call. The value is JSON.stringify'd for strings so quotes show in the message.
Source
Thrown at packages/@glimmer/syntax/lib/v2/normalize.ts:1073
}
}
function isLiteral(node: ASTv1.Expression): node is ASTv1.Literal {
switch (node.type) {
case 'StringLiteral':
case 'BooleanLiteral':
case 'NumberLiteral':
case 'UndefinedLiteral':
case 'NullLiteral':
return true;
default:
return false;
}
}
function assertIllegalLiteral(node: ASTv1.Literal, loc: SourceSpan): never {
let value = node.type === 'StringLiteral' ? JSON.stringify(node.value) : String(node.value);
throw generateSyntaxError(`Unexpected literal \`${value}\``, loc);
}
function printPath(node: ASTv1.PathExpression | ASTv1.CallNode): string {
if (node.type !== 'PathExpression' && node.path.type === 'PathExpression') {
return printPath(node.path);
} else {
return new Printer({ entityEncoding: 'raw' }).print(node);
}
}
function printHead(node: ASTv1.PathExpression | ASTv1.CallNode): string {
if (node.type === 'PathExpression') {
return node.head.original;
} else if (node.path.type === 'PathExpression') {
return printHead(node.path);
} else {
return new Printer({ entityEncoding: 'raw' }).print(node);
}View on GitHub (pinned to 26f97246a8)
Solutions
- Replace the literal with a variable/path, or define the helper and call it by name
- Quote via a helper if you need a literal string value (e.g. use a helper that returns the string)
- Fix typos: remove stray quotes around what should be an identifier
Example fix
// before
{{#each (items 'list') as |item|}}
// after
{{#each (get this "list") as |item|}} Defensive patterns
Strategy: validation
Validate before calling
// literal in callee position of a mustache/subexpression is illegal
function literalCallee(src) {
return /{{\s*['"0-9(][^}]*\s+\w+\s*\(/.test(src);
} Prevention
- Only paths/identifiers may be called in templates; wrap literals in a helper if needed
- ember-template-lint catches many literal-position mistakes
- After upgrades, run the build early to surface normalizer errors
When it happens
Trigger: Using a literal where a path is required, e.g. {{'foo' bar}} or ('str')() as a helper name in a subexpression like {{#each (lookup 'key')}} misuse patterns, or passing a bare literal in a position the normalizer's literal assertion covers.
Common situations: Handwriting dynamic-looking syntax with quoted helper names, migrating handlebars that relied on lenient parsers, or typos where a string was left where a variable was intended.
Related errors
- Changing context using "../" is not supported in Glimmer
- Mixing '.' and '/' in paths is not supported in Glimmer; use
- '.' is not a supported path in Glimmer; check for a path wit
- Attempted to parse a path expression, but it was not valid.
- Attempted to parse a path expression, but it was not valid.
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/344d7e6b787a982b.
Report an issue: GitHub.