emberjs/ember.js · error

An unquoted attribute value must be a string or a mustache,

Error message

An unquoted attribute value must be a string or a mustache, preceded by whitespace or a '=' character, and followed by whitespace, a '>' character, or '/>'

What it means

assembleAttributeValue, when assembling an unquoted attribute value from concatenated parts (text/mustache), only allows a single part (or a lone `/`). If more than one part remains after simplification — e.g. an unquoted value mixing text and mustaches — the value is invalid for unquoted form and this error is thrown.

Source

Thrown at packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts:617

  }

  assembleAttributeValue(
    parts: ASTv1.AttrPart[],
    isQuoted: boolean,
    isDynamic: boolean,
    span: src.SourceSpan
  ): ASTv1.AttrValue {
    if (isDynamic) {
      if (isQuoted) {
        return this.assembleConcatenatedValue(parts);
      } else {
        assertPresentArray(parts);

        const [head, a] = parts;
        if (a === undefined || (a.type === 'TextNode' && a.chars === '/')) {
          return head;
        } else {
          throw generateSyntaxError(
            `An unquoted attribute value must be a string or a mustache, ` +
              `preceded by whitespace or a '=' character, and ` +
              `followed by whitespace, a '>' character, or '/>'`,
            span
          );
        }
      }
    } else if (isPresentArray(parts)) {
      return parts[0];
    } else {
      return b.text({ chars: '', loc: span });
    }
  }
}

/**
  ASTPlugins can make changes to the Glimmer template AST before
  compilation begins.

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Quote the attribute value: `class="a{{x}}b"`
  2. Use a single mustache or single text node for unquoted values, e.g. `class={{x}}`
  3. Build the combined string in JS and pass one value instead

Example fix

// before
<div class=a{{x}}b>...</div>
// after
<div class="a{{x}}b">...</div>
Defensive patterns

Strategy: validation

Validate before calling

// unquoted attr values with interpolation are invalid
if (/<\w+\s+[\w-]+=[^\s"'>{]*\{\{/m.test(template)) {
  throw new Error('Quote attribute values containing mustaches');
}

Try / catch

try { parse(template) } catch (e) { if (/unquoted attribute value/.test(e.message)) { /* auto-quote the attribute and reparse */ } else throw e; }

Prevention

When it happens

Trigger: Unquoted attribute value containing a mustache plus extra text or a trailing slash, e.g. `<div class="a{{x}}">` written as `<div class=a{{x}}b>` or `<div foo=bar/>`-style oddities.

Common situations: Writing dynamic attribute values without quotes; forgetting quotes after refactoring `<div class={{x}}>rest`; shell-style `/` at the end of an unquoted value.

Related errors


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