emberjs/ember.js · error

Unexpected named block nested in a normal block

Error message

Unexpected named block nested in a normal block

What it means

Named blocks cannot be nested inside an ordinary block — i.e. inside `{{#if}}`, `{{#each}}`, or any other normal `{{#...}}` block. BlockChildren.assertBlock throws when named blocks appear among a normal block's children.

Source

Thrown at packages/@glimmer/syntax/lib/v2/normalize.ts:913

      (c): c is ASTv2.ContentNode => !(c instanceof ASTv2.NamedBlock)
    );
  }
}

class TemplateChildren extends Children {
  assertTemplate(table: ProgramSymbolTable): ASTv2.Template {
    if (isPresentArray(this.namedBlocks)) {
      throw generateSyntaxError(`Unexpected named block at the top-level of a template`, this.loc);
    }

    return this.block.builder.template(table, this.nonBlockChildren, this.block.loc(this.loc));
  }
}

class BlockChildren extends Children {
  assertBlock(table: BlockSymbolTable): ASTv2.Block {
    if (isPresentArray(this.namedBlocks)) {
      throw generateSyntaxError(`Unexpected named block nested in a normal block`, this.loc);
    }

    return this.block.builder.block(table, this.nonBlockChildren, this.loc);
  }
}

class ElementChildren extends Children {
  constructor(
    private el: BuildElement,
    loc: SourceSpan,
    children: (ASTv2.ContentNode | ASTv2.NamedBlock)[],
    block: BlockContext
  ) {
    super(loc, children, block);
  }

  assertNamedBlock(name: SourceSlice, table: BlockSymbolTable): ASTv2.NamedBlock {
    if (this.el.base.selfClosing) {

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Move the named block to be a direct child of the element/component tag it belongs to.
  2. To make a named block conditional, put the condition inside the named block, not around it.
  3. Split into multiple invocations if different named block sets are needed per condition.

Example fix

// before
<Card>
  {{#if this.showHeader}}
    <:header>Hi</:header>
  {{/if}}
</Card>

// after
<Card>
  <:header>{{#if this.showHeader}}Hi{{/if}}</:header>
</Card>
Defensive patterns

Strategy: validation

Validate before calling

// Named blocks must be direct children of a tag, not inside {{#blocks}}.
function assertNamedBlocksNotNested(children) {
  for (const c of children) {
    if (c.type === 'block' && c.children.some(x => x.type === 'named-block')) {
      throw new Error('Named block nested inside a normal {{#block}}');
    }
  }
}

Prevention

When it happens

Trigger: Compiling a template where `<:name>...</:name>` appears inside e.g. `{{#if cond}} ... <:name> ... {{/if}}`.

Common situations: Conditionally wrapping named blocks with `{{#if}}`; accidentally placing a named block after another block's content but still inside its curlies; copy-paste refactors.

Related errors


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