emberjs/ember.js · error

In ${tag}, ${modifier} is not a valid modifier

Error message

In ${tag}, ${modifier} is not a valid modifier

What it means

Element modifiers in Glimmer templates must be identifier paths (helpers like {{on "click" ...}} or {{action ...}}), never Handlebars literals. When a mustache used as an element modifier ({{...}} inside a tag) has a literal path, addElementModifier builds a synthetic tag snippet and throws a syntax error, since a literal cannot resolve to a modifier.

Source

Thrown at packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts:670

    : b.hash({
        pairs: [],
        loc: compiler.source.spanFor(end).collapse('end'),
      });

  return { path, params, hash };
}

function addElementModifier(
  element: ParserNodeBuilder<StartTag>,
  mustache: ASTv1.MustacheStatement
) {
  const { path, params, hash, loc } = mustache;

  if (isHBSLiteral(path)) {
    const modifier = `{{${printLiteral(path)}}}`;
    const tag = `<${element.name} ... ${modifier} ...`;

    throw generateSyntaxError(`In ${tag}, ${modifier} is not a valid modifier`, mustache.loc);
  }

  const modifier = b.elementModifier({ path, params, hash, loc });
  element.modifiers.push(modifier);
}

function repairBlock(
  source: src.Source,
  block: HBS.UpstreamBlockStatement,
  fallbackStart: SourceSpan
): HBS.BlockStatement {
  // Extend till the beginning of the block
  if (!block.program.loc) {
    const start = block.program.body.at(0);
    const end = block.program.body.at(-1);

    if (start && end) {
      block.program.loc = {

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Use a real modifier helper path: <button {{on "click" this.save}}>Change</button> or <form {{action "submit" on="submit"}}> in classic Ember.
  2. Unquote the modifier name if it was accidentally quoted: {{"action" ...}} → {{action ...}}.
  3. Remove the stray literal from the tag if it was leftover text, and validate the template with ember-template-lint to catch it earlier.

Example fix

// before
<button {{"onclick"}}></button>

// after
<button {{on "click" this.handleClick}}>Save</button>
Defensive patterns

Strategy: validation

Validate before calling

// reject literal mustaches used as element modifiers
function assertValidModifiers(template) {
  const bad = template.match(/<[A-Za-z][^>]*{{\s*("[^"]*"|'[^']*'|\d+|true|false|null)\s*[^}]*}}[^>]*>/g);
  if (bad) throw new Error(`Literal is not a valid modifier: ${bad.join(', ')}`);
}
assertValidModifiers(template);

Prevention

When it happens

Trigger: Writing a literal inside an element tag as if it were a modifier: <div {{"foo"}}></div>, <input {{42}} />, <button {{true}}></button>. Thrown in addElementModifier (called from MustacheStatement handling) when isHBSLiteral(path) is true.

Common situations: Typos where a modifier name was deleted or quoted by accident (<div {{"on" "click" ...}}); copy/paste mangling of the {{on ...}} / {{action ...}} syntax; template editors auto-quoting identifiers.

Related errors


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