facebook/flow · error

SimpleTransform.transformProgram: Expected program node.

Error message

SimpleTransform.transformProgram: Expected program node.

What it means

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.).

Solutions

  1. Guard deletion/replacement logic so the Program node is never removed: if (node.type === 'Program') return node;
  2. If you must rebuild the root, return a valid Program node (type: 'Program' with a body array).
  3. Use SimpleTransform.transform (which allows null) instead of transformProgram when root removal is intentional, and handle null yourself.

Example fix

// before
transform(node) {
  if (shouldDelete(node)) return null; // shouldDelete matches Program -> throws
}

// after
transform(node) {
  if (node.type !== 'Program' && shouldDelete(node)) return null;
  return node;
}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the visitor never nulls the root
function guarded(transform) {
  return (node) => (node.type === 'Program' ? node : transform(node));
}
const out = SimpleTransform.transformProgram(program, {transform: guarded(myTransform)});

Type guard

function isProgramNode(v) {
  return v != null && typeof v === 'object' && v.type === 'Program';
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/c0678ffb39d7f698. Report an issue: GitHub.

Appendix: 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 f88ac94bcf)