facebook/flow · error · Error
SimpleTransform.transformProgram: Expected program node.
Error message
SimpleTransform.transformProgram: Expected program node.
What it means
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.
Source
Thrown at packages/flow-parser/oxidized-src/transform/SimpleTransform.js:147
/**
* Transform the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static transform(node: ESNode, options: TransformOptions): ESNode | null {
return new SimpleTransform().transform(node, options);
}
static transformProgram(
program: Program,
options: TransformOptions,
): Program {
const result = SimpleTransform.transform(program, options);
if (result?.type === 'Program') {
return result;
}
throw new Error('SimpleTransform.transformProgram: Expected program node.');
}
/**
* Return a new AST node with the given properties overrided if needed.
*
* This function takes care to only create new nodes when needed. Referential equality of nodes
* is important as its used to know if a node should be re-traversed.
*
* @param node The base AST node.
* @param overrideProps New properties to apply to the node.
* @return Either the orginal node if the properties matched the existing node or a new node with
* the new properties.
*/
static nodeWith<T extends ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: VisitorKeysType,
): T {View on GitHub (pinned to d1341dac89)
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
Example fix
// before
visitor.transform = (node) => {
if (node.type === 'Program') return null; // removal -> throws
}
// after
visitor.transform = (node) => {
if (node.type === 'Program') {
return {...node, body: node.body.filter(notStripped)};
}
} Defensive patterns
Strategy: validation
Validate before calling
function assertProgramRoot(ast) {
if (ast == null || ast.type !== 'Program') {
throw new Error('Expected Program root, got ' + (ast && ast.type));
}
}
assertProgramRoot(ast);
const out = SimpleTransform.transformProgram(ast, options); Type guard
const isProgram = (n) => n != null && n.type === 'Program';
Try / catch
try {
out = SimpleTransform.transformProgram(program, options);
} catch (e) {
if (e.message.includes('Expected program node')) {
// the visitor removed/replaced the root: fix the visitor, not the call site
throw new Error('Transform dropped the Program root; check the Program visitor case');
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- SimpleTransform: invalid array result for root node
- Expected parent node to be set on "${target.type}"
- Expected to find the ${target.type} as a direct child of the
- Cannot insert array into non-array parent type: ${parent.typ
- No visitor keys found for node type "${node.type}".
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/c0678ffb39d7f698.
Report an issue: GitHub.