emberjs/ember.js · error · TraversalError
Replacing and removing in key handlers is not yet supported.
Error message
Replacing and removing in key handlers is not yet supported.
What it means
During traversal, a visitor's key 'enter' handler returned a value, meaning it tried to replace or remove the node. Traversal keyed handlers (per-property) don't support replacement/removal yet, so visitKey throws. This is a visitor API limitation, not template syntax.
Source
Thrown at packages/@glimmer/syntax/lib/traversal/traverse.ts:182
let value = get(node, key);
if (!value) {
return;
}
let keyEnter: KeyHandler<N, VisitorKeys[N['type']] & keyof N> | undefined;
let keyExit: KeyHandler<N, VisitorKeys[N['type']] & keyof N> | undefined;
if (handler !== undefined) {
let keyHandler = getKeyHandler(handler, key);
if (keyHandler !== undefined) {
keyEnter = getEnterFunction(keyHandler);
keyExit = getExitFunction(keyHandler);
}
}
if (keyEnter !== undefined) {
if (keyEnter(node, key) !== undefined) {
throw cannotReplaceOrRemoveInKeyHandlerYet(node, key);
}
}
if (Array.isArray(value)) {
visitArray(visitor, value, path, key);
} else {
let keyPath = new WalkerPath(value, path, key);
let result = visitNode(visitor, keyPath);
if (result !== undefined) {
// TODO: dynamically check the results by having a table of
// expected node types in value space, not just type space
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assignKey(node, key, value, result as any);
}
}
if (keyExit !== undefined) {View on GitHub (pinned to 26f97246a8)
Solutions
- Return undefined from the key handler; perform replacement in the node-level handler instead
- Use the node handler (per node type) where replacement/removal IS supported
- Mutate the node in place rather than returning a replacement
Example fix
// before
visit(node, { keys: { parts: (node, key) => [] } }) // returns value -> throws
// after
visit(node, { Block: (node) => ({ ...node, parts: [] }) }) // replace via node handler Defensive patterns
Strategy: type-guard
Validate before calling
// assert key handlers never return a value
function safeKeyHandler(fn) { return (node, key) => { const r = fn(node, key); if (r !== undefined) throw new Error('key handlers cannot replace/remove: return undefined'); }; } Type guard
const returnsNothing = (fn) => fn.length >= 0 && undefined; // wrap: (node,key)=>{ fn(node,key); return undefined; } Try / catch
try { visit(ast, visitor) } catch (e) { if (/Replacing and removing in key handlers/.test(e.message)) { /* fall back to node-level handler transform */ } else throw e; } Prevention
- Key handlers must return undefined; only node handlers may replace/remove
- Write a unit test for your visitor that traverses a sample AST
- Mutate nodes in place instead of returning replacements in key handlers
When it happens
Trigger: Writing a NodeVisitor whose handler for a specific key (via #visit in the traversal API, e.g. visiting Key ASTv1 nodes keyed by property) returns a non-undefined value from the key enter callback.
Common situations: Writing codemods/transformers over Glimmer ASTs; assuming visitor return values replace nodes like in other AST tools (e.g. babel); porting node-level visitors to key-level handlers.
Related errors
- b.concat requires at least one part
- Compile Error: ${template.problem} @ ${template.span.start}.
- A resolved helper cannot be passed as a named argument as th
- deprecation override for ${id} not found
- You must pass both the owner and args to super() in your com
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/1afd5c5b5b6c2dad.
Report an issue: GitHub.