{"record":{"id":"c972008bacb9b312","repo":"emberjs/ember.js","slug":"node-type-requires-name","errorCode":null,"errorMessage":"${node.type} requires ${name}","messagePattern":"(.+?) requires (.+?)","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"packages/@handlebars/parser/lib/visitor.js","lineNumber":37,"sourceCode":"          'Unexpected node type \"' +\n            value.type +\n            '\" found when accepting ' +\n            name +\n            ' on ' +\n            node.type\n        );\n      }\n      node[name] = value;\n    }\n  },\n\n  // Performs an accept operation with added sanity check to ensure\n  // required keys are not removed.\n  acceptRequired: function (node, name) {\n    this.acceptKey(node, name);\n\n    if (!node[name]) {\n      throw new Exception(node.type + ' requires ' + name);\n    }\n  },\n\n  // Traverses a given array. If mutating, empty responses will be removed\n  // for child elements.\n  acceptArray: function (array) {\n    for (let i = 0, l = array.length; i < l; i++) {\n      this.acceptKey(array, i);\n\n      if (!array[i]) {\n        array.splice(i, 1);\n        i--;\n        l--;\n      }\n    }\n  },\n\n  accept: function (object) {","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@handlebars/parser/lib/visitor.js#L19-L55","documentation":"acceptRequired traverses a required key and then asserts the key still holds a truthy value — guarding against transforms or visitors that delete required child nodes (like a BlockStatement's path or program). If the key is empty after traversal, the node is structurally invalid and it throws '<NodeType> requires <name>'.","triggerScenarios":"A mutating visitor returns null/undefined or deletes node[name] for a required key (e.g. removing node.path or node.program from a BlockStatement) during traversal.","commonSituations":"Codemods/whitespace-strip passes removing 'empty' nodes that are actually required; custom plugins returning undefined from handlers; refactor tools rewriting AST sections.","solutions":["Stop deleting required keys in your visitor; return the node unchanged if you meant 'no-op'","Replace the node's parent entry instead of emptying a required field","Ensure handler functions return a valid replacement node, not undefined","Add an AST assertion pass after your transform to catch corruption early"],"exampleFix":"// before\nvisit: { PathExpression() { return null; } }\n// after\nvisit: { PathExpression(node) { return node; } }","handlingStrategy":"validation","validationCode":"const REQUIRED = { BlockStatement: ['path','program'], PartialBlockStatement: ['path','program'], Program: ['body'] };\nObject.entries(REQUIRED).forEach(([t, keys]) => keys.forEach(k => { if (astNodes.some(n => n.type === t && !n[k])) throw new Error(t + ' requires ' + k); }));","typeGuard":null,"tryCatchPattern":"try { visitor.visit(ast); } catch (e) { if (/ requires /.test(e.message)) { console.error('Transform removed a required key:', e.message); } throw e; }","preventionTips":["Never delete or null out required AST keys; replace whole nodes instead","Return the original node from visitor handlers for no-ops","Add post-transform AST assertions in tests"],"tags":["ast","visitor","handlebars","parser"],"backgroundTag":"invalid-ast-structure","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}