facebook/flow · error · Error

flowImportTo: Unexpected transform result.

Error message

flowImportTo: Unexpected transform result.

What it means

flowImportTo runs SimpleTransform.transform over the passed AST and requires the result to be a non-null Program node; anything else is an invalid transform result and throws. The built-in visitor only rewrites module specifiers of import/export declarations, so in practice this throw means the root you passed was not a Program or a custom transform removed the root.

Source

Thrown at packages/flow-api-translator/src/flowImportTo.js:70

        case 'DeclareExportAllDeclaration':
        case 'ExportAllDeclaration':
        case 'DeclareExportDeclaration':
        case 'ExportNamedDeclaration': {
          if (node.source != null) {
            return SimpleTransform.nodeWith(node, {
              source: mapSource(node.source),
            });
          }
          return node;
        }
        default: {
          return node;
        }
      }
    },
  });
  if (result == null || result.type !== 'Program') {
    throw new Error('flowImportTo: Unexpected transform result.');
  }
  return result;
}

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Pass the Program node itself: const {type: 'Program'} AST from flow-parser parse(), not a child or wrapper node.
  2. Prefer the public translateFlowImportsTo API, which parses and drives flowImportTo correctly.
  3. If you forked the visitor, ensure every branch (especially for Program) returns a single ESNode, never null or an array.
  4. Add an assertion ast.type === 'Program' before calling to fail with a clearer message.

Example fix

// before
const result = flowImportTo(ast.body[0], code, scopeManager, opts);

// after
const ast = parse(code, {sourceType: 'module', flow: 'all'});
const result = flowImportTo(ast, code, scopeManager, opts);
Defensive patterns

Strategy: validation

Validate before calling

function isProgram(node) {
  return node != null && typeof node === 'object' && node.type === 'Program';
}
// before calling:
if (!isProgram(ast)) throw new TypeError('flowImportTo requires the Program node');

Type guard

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

Try / catch

try {
  renamed = flowImportTo(ast, code, scopeManager, {sourceMapper});
} catch (e) {
  if (e.message.startsWith('flowImportTo:')) {
    // root was not a Program or a custom transform removed it
    throw new Error(`flowImportTo got ${ast?.type ?? 'null'}; pass the parsed Program node`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling flowImportTo directly (or translateFlowImportsTo) with a non-Program root, e.g. a File wrapper, a Statement, or an AST produced by a prior transform whose root was replaced/removed. Any visitor returning null or an array for the Program node also triggers it.

Common situations: Pipelines that chain transforms and accidentally hand flowImportTo a subtree instead of the whole Program; custom forks of the visitor that drop nodes at the root; passing an AST from a different parser whose root type is not 'Program'.

Related errors


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