emberjs/ember.js · error

Invalid block parameters syntax: block parameters must be pr

Error message

Invalid block parameters syntax: block parameters must be preceded by the `as` keyword

What it means

Glimmer's parser encountered an attribute-like token starting with a pipe character (e.g. `<Hello |foo|>`) that is not part of a valid block params form. Block parameters must be introduced with the `as` keyword (`<Hello as |foo|>`); a leading `|` without it is assumed to be a user mistake with block-params syntax, so the parser throws a syntax error instead of silently treating it as an attribute.

Source

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

  finishAttributeValue(): void {
    this.finalizeTextPart();

    let tag = this.currentTag;
    let tokenizerPos = this.offset();

    if (tag.type === 'EndTag') {
      throw generateSyntaxError(
        `Invalid end tag: closing tag must not have attributes`,
        this.source.spanFor({ start: tag.start.toJSON(), end: tokenizerPos.toJSON() })
      );
    }

    let { name, parts, start, isQuoted, isDynamic, valueSpan } = this.currentAttr;

    // Just trying to be helpful with `<Hello |foo|>` rather than letting it through as an attribute
    if (name.startsWith('|') && parts.length === 0 && !isQuoted && !isDynamic) {
      throw generateSyntaxError(
        'Invalid block parameters syntax: block parameters must be preceded by the `as` keyword',
        start.until(start.move(name.length))
      );
    }

    let value = this.assembleAttributeValue(parts, isQuoted, isDynamic, start.until(tokenizerPos));
    value.loc = valueSpan.withEnd(tokenizerPos);

    let attribute = b.attr({ name, value, loc: start.until(tokenizerPos) });

    this.currentStartTag.attributes.push(attribute);
  }

  private parsePossibleBlockParams() {
    // const enums that we can't use directly
    const BEFORE_ATTRIBUTE_NAME = 'beforeAttributeName' as TokenizerState.beforeAttributeName;
    const ATTRIBUTE_NAME = 'attributeName' as TokenizerState.attributeName;
    const AFTER_ATTRIBUTE_NAME = 'afterAttributeName' as TokenizerState.afterAttributeName;

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Add the `as` keyword before the pipes: `<Hello as |foo|>`
  2. If you intended an attribute, rename it so it does not start with `|`
  3. Quote the value if a pipe character is genuinely part of an attribute value

Example fix

// before
<Hello |name|>Hi {{name}}</Hello>
// after
<Hello as |name|>Hi {{name}}</Hello>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate template tag opening forms: any `|` after a tag name must follow `as `
if (/<[^>\n]*\s\|/.test(tagOpen) && !/<[^>\n]*\bas\s\|/.test(tagOpen)) {
  throw new Error('Block params must use `as |name|`');
}

Prevention

When it happens

Trigger: In finishAttributeValue, when the current tag attribute name starts with '|', is unquoted, is not dynamic, and has zero parts — i.e. a template contains something like `<Component |param|>` without the preceding `as`.

Common situations: Typing `<Foo |bar|>` out of habit from Ruby-style blocks; copy-pasting older `<Foo |a b|>` syntax that predates the `as` requirement; forgetting `as` when converting a normal attribute into block params.

Related errors


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