{"record":{"id":"c0678ffb39d7f698","repo":"facebook/flow","slug":"simpletransform-transformprogram-expected-program","errorCode":null,"errorMessage":"SimpleTransform.transformProgram: Expected program node.","messagePattern":"SimpleTransform\\.transformProgram: Expected program node\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/flow-parser/oxidized-src/transform/SimpleTransform.js","lineNumber":147,"sourceCode":"\n  /**\n   * Transform the given AST tree.\n   * @param node The root node to traverse.\n   * @param options The option object.\n   */\n  static transform(node: ESNode, options: TransformOptions): ESNode | null {\n    return new SimpleTransform().transform(node, options);\n  }\n\n  static transformProgram(\n    program: Program,\n    options: TransformOptions,\n  ): Program {\n    const result = SimpleTransform.transform(program, options);\n    if (result?.type === 'Program') {\n      return result;\n    }\n    throw new Error('SimpleTransform.transformProgram: Expected program node.');\n  }\n\n  /**\n   * Return a new AST node with the given properties overrided if needed.\n   *\n   * This function takes care to only create new nodes when needed. Referential equality of nodes\n   * is important as its used to know if a node should be re-traversed.\n   *\n   * @param node The base AST node.\n   * @param overrideProps New properties to apply to the node.\n   * @return Either the orginal node if the properties matched the existing node or a new node with\n   *         the new properties.\n   */\n  static nodeWith<T extends ESNode>(\n    node: T,\n    overrideProps: Partial<T>,\n    visitorKeys?: VisitorKeysType,\n  ): T {","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/facebook/flow/blob/d1341dac899a79c027762f6b423d896045287620/packages/flow-parser/oxidized-src/transform/SimpleTransform.js#L129-L165","documentation":"SimpleTransform.transformProgram is the typed wrapper around SimpleTransform.transform: it requires the transform to still yield a node whose type is 'Program'. The error fires when the result is null (root was removed) or a node of another type (root was replaced). It exists because everything downstream of transformProgram (printers, codemod pipelines) assumes a Program root.","triggerScenarios":"A visitor returns null for the Program node (removal), or returns a node of a different type (e.g. an Expression or a custom node) as the new root; also when the input AST's root is not a Program in the first place and survives the transform unchanged.","commonSituations":"A visitor meant to strip some statements accidentally removes or replaces the root; reusing a visitor written for expression-level transforms on a whole program; passing a Babel-style File AST (root type 'File') instead of an ESTree Program.","solutions":["Never remove or replace the root Program in a visitor; mutate its body instead","Ensure the AST you pass in is an ESTree Program from flow-parser, not a File wrapper from another parser","If you only need statement-level changes, return {...program, body: filteredBody} from the Program case"],"exampleFix":"// before\nvisitor.transform = (node) => {\n  if (node.type === 'Program') return null; // removal -> throws\n}\n\n// after\nvisitor.transform = (node) => {\n  if (node.type === 'Program') {\n    return {...node, body: node.body.filter(notStripped)};\n  }\n}","handlingStrategy":"validation","validationCode":"function assertProgramRoot(ast) {\n  if (ast == null || ast.type !== 'Program') {\n    throw new Error('Expected Program root, got ' + (ast && ast.type));\n  }\n}\nassertProgramRoot(ast);\nconst out = SimpleTransform.transformProgram(ast, options);","typeGuard":"const isProgram = (n) => n != null && n.type === 'Program';","tryCatchPattern":"try {\n  out = SimpleTransform.transformProgram(program, options);\n} catch (e) {\n  if (e.message.includes('Expected program node')) {\n    // the visitor removed/replaced the root: fix the visitor, not the call site\n    throw new Error('Transform dropped the Program root; check the Program visitor case');\n  }\n  throw e;\n}","preventionTips":["Verify ast.type === 'Program' before calling transformProgram","Never return null or a non-Program node from the root visitor case","Keep expression-level transforms away from transformProgram; use transform() for them"],"tags":["ast","transform","program-node","flow-parser"],"backgroundTag":"invalid-visitor-return-value","analyzedSha":"d1341dac899a79c027762f6b423d896045287620","analyzedAt":"2026-08-17T00:07:02.212Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}