{"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":"validation","errorClass":null,"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/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/packages/flow-parser/oxidized-src/transform/SimpleTransform.js#L129-L165","documentation":"SimpleTransform.transformProgram wraps SimpleTransform.transform and requires the result to be a node with type 'Program'. If the visitor removed the Program (returned null) or replaced it with a different node type, the wrapper throws because downstream code assumes a Program shape (body, etc.).","triggerScenarios":"SimpleTransform.transformProgram(program, {transform}) with a visitor that returns null for the Program node or converts it to another node type (e.g. wrapping into a custom File node, or returning a body statement by mistake).","commonSituations":"Visitors meant to delete nodes that match too broadly (e.g. a delete-if predicate that also matches Program); refactorings that rebuild the root as a different structure; chained transforms where an earlier step already replaced the root.","solutions":["Guard deletion/replacement logic so the Program node is never removed: if (node.type === 'Program') return node;","If you must rebuild the root, return a valid Program node (type: 'Program' with a body array).","Use SimpleTransform.transform (which allows null) instead of transformProgram when root removal is intentional, and handle null yourself."],"exampleFix":"// before\ntransform(node) {\n  if (shouldDelete(node)) return null; // shouldDelete matches Program -> throws\n}\n\n// after\ntransform(node) {\n  if (node.type !== 'Program' && shouldDelete(node)) return null;\n  return node;\n}","handlingStrategy":"validation","validationCode":"// Ensure the visitor never nulls the root\nfunction guarded(transform) {\n  return (node) => (node.type === 'Program' ? node : transform(node));\n}\nconst out = SimpleTransform.transformProgram(program, {transform: guarded(myTransform)});","typeGuard":"function isProgramNode(v) {\n  return v != null && typeof v === 'object' && v.type === 'Program';\n}","tryCatchPattern":null,"preventionTips":["Make delete-predicates exclude Program by construction.","Use SimpleTransform.transform and handle a null result yourself when root removal is intentional."],"tags":["flow","parser","ast","transform","simpletransform"],"backgroundTag":"ast-transform-invalid-root","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","contentChangedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}