emberjs/ember.js · error
Unexpected named block at the top-level of a template
Error message
Unexpected named block at the top-level of a template
What it means
A top-level template (program) may only contain regular content; named blocks (`<:name>`) are only valid inside another element/component invocation as yielded blocks. TemplateChildren.assertTemplate throws when named blocks appear at the program's top level.
Source
Thrown at packages/@glimmer/syntax/lib/v2/normalize.ts:903
case 'HtmlComment':
return false;
case 'HtmlText':
return !/^\s*$/u.test(c.chars);
default:
return true;
}
}).length
);
this.nonBlockChildren = children.filter(
(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(View on GitHub (pinned to 26f97246a8)
Solutions
- Move the named block inside the element/component that yields it, e.g. `<Modal><:header>...</:header></Modal>`.
- If the content should be at the top level, remove the `<:name>` wrapper and keep plain content.
- If yielding multiple sections, use `{{yield to="name"}}` with `{{#if (has-block "name")}}` in the component instead of top-level named blocks.
Example fix
// before <:header>Title</:header> // after <Card> <:header>Title</:header> <:body>Content</:body> </Card>
Defensive patterns
Strategy: validation
Validate before calling
// Template roots must not contain named blocks.
function assertNoTopLevelNamedBlocks(templateTokens) {
if (templateTokens.some(t => t.type === 'named-block')) {
throw new Error('Named blocks are not allowed at template top level');
}
} Prevention
- Remember named blocks only belong inside element/component tags.
- Wrap multi-section content in a component invocation that yields to names.
- Review refactors that remove component tags so leftover blocks are deleted too.
When it happens
Trigger: Writing `<:header>...</:header>` at the top level of a template file rather than nested inside a component tag.
Common situations: Copy-pasting named block syntax out of a component invocation; misunderstanding that `<:yield>`-style blocks belong to elements, not the template root; refactoring a component call away and leaving its named blocks behind.
Related errors
- Unexpected named block nested in a normal block
- <:${name.chars}/> is not a valid named block: named blocks c
- Unexpected named block inside <:${name.chars}> named block:
- named block <:${name.chars}> cannot have attributes, argumen
- You used ${variable}.${tail.join('.')} as a tag name, but ${
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/7365ab8bb5bdf5ad.
Report an issue: GitHub.