emberjs/ember.js · error

Attempted to parse a path expression, but it was not valid.

Error message

Attempted to parse a path expression, but it was not valid. Paths must start with a-z or A-Z.

What it means

For regular (non-@, non-data) paths, the parser shifts the head segment; if the path has no leading head it cannot be resolved as a variable, so the parser throws. Valid plain paths must start with a letter a-z or A-Z (e.g. this.foo, items, someVar) — the head becomes a variable reference (b.var).

Source

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

      if (head === undefined) {
        throw generateSyntaxError(
          `Attempted to parse a path expression, but it was not valid. Paths beginning with @ must start with a-z.`,
          this.source.spanFor(path.loc)
        );
      }

      pathHead = b.atName({
        name: `@${head}`,
        loc: this.source.spanFor({
          start: path.loc.start,
          end: { line: path.loc.start.line, column: path.loc.start.column + head.length + 1 },
        }),
      });
    } else {
      const head = parts.shift();

      if (head === undefined) {
        throw generateSyntaxError(
          `Attempted to parse a path expression, but it was not valid. Paths must start with a-z or A-Z.`,
          this.source.spanFor(path.loc)
        );
      }

      pathHead = b.var({
        name: head,
        loc: this.source.spanFor({
          start: path.loc.start,
          end: { line: path.loc.start.line, column: path.loc.start.column + head.length },
        }),
      });
    }

    return b.path({
      head: pathHead,
      tail: parts,
      loc: this.source.spanFor(path.loc),

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Provide the intended variable name at the indicated location: replace the empty/invalid path with a real identifier starting with a letter (e.g. {{this.foo}}).
  2. If you meant the current context, write {{this}} explicitly rather than an empty path.
  3. Re-run or fix the preprocessor/codemod that produced the empty path and inspect its input.

Example fix

// before
{{}}

// after
{{this.title}}
Defensive patterns

Strategy: validation

Validate before calling

// validate plain paths have a letter head before compiling
function assertValidPathHeads(template) {
  const exprs = template.match(/{{([^}]*)}}/g) || [];
  for (const e of exprs) {
    const inner = e.slice(2, -2).trim();
    if (inner && !/^[a-zA-Z@("']/.test(inner)) throw new Error(`Expression must start with a-z or A-Z: ${e}`);
  }
}
assertValidPathHeads(template);

Prevention

When it happens

Trigger: Compiling a template with a PathExpression whose parts list is empty/undefined after stripping data/source markers — e.g. a path expression that normalizes to an empty string reaching the visitor's else branch, where parts.shift() returns undefined.

Common situations: Malformed output from template preprocessors/codemods that drop the identifier; hand-edited templates leaving an empty expression like {{ }} that survives earlier validation; macros building paths programmatically with an empty head.

Related errors


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