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

  1. Replace the literal with a variable/path, or define the helper and call it by name
  2. Quote via a helper if you need a literal string value (e.g. use a helper that returns the string)
  3. 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

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


AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01). Data as JSON: /api/errors/344d7e6b787a982b. Report an issue: GitHub.