emberjs/ember.js · error · Exception
Unexpected node type "${value.type}" found when accepting ${
Error message
Unexpected node type "${value.type}" found when accepting ${name} on ${node.type} What it means
The visitor's acceptKey sanity check (active when the visitor is mutating) verifies that any node produced while traversing a key has a registered visitor handler. If a child value has an unknown node type, it throws rather than silently corrupting the AST during mutation passes.
Source
Thrown at packages/@handlebars/parser/lib/visitor.js:18
import Exception from './exception.js';
function Visitor() {
this.parents = [];
}
Visitor.prototype = {
constructor: Visitor,
mutating: false,
// Visits a given value. If mutating, will replace the value if necessary.
acceptKey: function (node, name) {
let value = this.accept(node[name]);
if (this.mutating) {
// Hacky sanity check: This may have a few false positives for type for the helper
// methods but will generally do the right thing without a lot of overhead.
if (value && !Visitor.prototype[value.type]) {
throw new Exception(
'Unexpected node type "' +
value.type +
'" found when accepting ' +
name +
' on ' +
node.type
);
}
node[name] = value;
}
},
// Performs an accept operation with added sanity check to ensure
// required keys are not removed.
acceptRequired: function (node, name) {
this.acceptKey(node, name);
if (!node[name]) {View on GitHub (pinned to 26f97246a8)
Solutions
- Fix the transform to only insert nodes with valid, registered types (e.g. 'MustacheStatement', 'PathExpression')
- Check the type string for typos on hand-constructed nodes
- Update visitor/transform packages so versions match the parser AST spec
- Temporarily run non-mutating to inspect the AST and find the offending node
Example fix
// before
node.params.push({ type: 'mustache', original: 'x' });
// after
node.params.push({ type: 'SubExpression', path: { type: 'PathExpression', original: 'x', data: false, depth: 0, parts: ['x'], loc: null }, params: [], hash: { type: 'Hash', pairs: [], loc: null }, loc: null }); Defensive patterns
Strategy: validation
Validate before calling
const VALID = new Set(['Program','MustacheStatement','BlockStatement','PartialBlockStatement','PartialStatement','CommentStatement','MustacheCommentStatement','DecoratorBlock','Decorator','ElementNode','AttrNode','TextNode','ConcatStatement','SubExpression','PathExpression','Hash','HashPair','BooleanLiteral','NumberLiteral','StringLiteral','UndefinedLiteral','NullLiteral','Block']);
function validNode(n) { return n == null || VALID.has(n.type); } Type guard
function isAstNode(v) { return v != null && typeof v.type === 'string' && Visitor.prototype[v.type] !== undefined; } Try / catch
try { visitor.visit(ast); } catch (e) { if (/Unexpected node type/.test(e.message)) { dumpAstAtError(ast, e.message); } throw e; } Prevention
- Only insert nodes whose type exists in the visitor map
- Copy node shapes from the parser's own output, don't hand-roll
- Keep transform/parser package versions in sync
- Run non-mutating traversal in tests to validate ASTs
When it happens
Trigger: A mutating Visitor pass (whitespace control, transforms, ember-template-recast-style edits) sets a node key to a value whose .type has no Visitor.prototype handler, or hand-builds AST nodes with a typo'd type field.
Common situations: Custom AST transforms inserting malformed nodes; plugins/glint/codemods operating on templates; version mismatch where a new node type isn't handled by an older visitor.
Related errors
- ${node.type} requires ${name}
- Unknown type: ${object.type}
- ${open.path.original} doesn't match ${close}
- Invalid path: ${original}
- Unexpected inverse block on decorator
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/b1aa54d392cac8a0.
Report an issue: GitHub.