facebook/flow · error · Error
unable to resolve scope
Error message
unable to resolve scope
What it means
Thrown inside flowDefToTSDef.js while checking whether a name resolves to a React import (isReactImport). It walks up from scopeNode via node.parent calling scopeManager.acquire(node, true) until a scope is found; if the whole parent chain yields no scope it throws 'unable to resolve scope'. This happens when the AST node has no parent pointers or the scopeManager was built from a different AST than the node belongs to.
Source
Thrown at packages/flow-api-translator/src/flowDefToTSDef.js:320
return globalScope.childScopes[0];
}
return globalScope;
})();
function isReactImport(scopeNode: FlowESTree.ESNode, name: string): boolean {
let currentScope = (() => {
let scope = null;
let node: FlowESTree.ESNode = scopeNode;
while (!scope && node) {
scope = scopeManager.acquire(node, true);
node = node.parent;
}
return scope;
})();
if (currentScope == null) {
throw new Error('unable to resolve scope');
}
const variableDef = (() => {
while (currentScope != null) {
for (const variable of currentScope.variables) {
if (variable.defs.length && variable.name === name) {
return variable;
}
}
currentScope = currentScope.upper;
}
})();
// No variable found, it is not imported.
// It could be a global though if isValidReactImportOrGlobal returns true.
if (variableDef == null) {
return false;
}View on GitHub (pinned to 5c86586199)
Solutions
- Use the public APIs (translateFlowDefToTSDef / translateFlowToTSDef from flow-api-translator) which build the AST and scopeManager together.
- If calling internals, re-parse the code and re-run scope analysis so every node reachable from scopeNode has parent pointers and an acquirable scope.
- Verify the scopeManager and AST come from the same source string before invoking translation.
- Wrap the call in try/catch and surface the offending node location for triage.
Example fix
// before
const {flowDefToTSDef} = require('flow-api-translator/src/flowDefToFlowDef.js');
const ast = parse(code); // no parents, no scope analysis
flowDefToTSDef(ast, code, someOtherScopeManager, opts);
// after
import {translateFlowDefToTSDef} from 'flow-api-translator';
const result = await translateFlowDefToTSDef({code, recoverFromErrors: true}); Defensive patterns
Strategy: validation
Validate before calling
// Verify the node can reach a scope before translating
function nodeResolvesToScope(scopeManager, node) {
let cur = node;
while (cur) {
if (scopeManager.acquire(cur, true) != null) return true;
cur = cur.parent;
}
return false;
} Type guard
function hasParentChain(node, upTo = 100) {
let cur = node, i = 0;
while (cur && i++ < upTo) cur = cur.parent;
return cur != null;
} Try / catch
try {
const out = await translateFlowDefToTSDef({code, recoverFromErrors: true});
} catch (e) {
if (e.message === 'unable to resolve scope') {
throw new Error('AST/scopeManager mismatch: re-parse and re-analyze the same source before translating');
}
throw e;
} Prevention
- Use public translate* APIs instead of internal modules so parse + scope analysis stay coherent.
- Never mix a scopeManager from one parse with an AST from another.
- Re-run scope analysis after any custom transform that rebuilds nodes, so parent links stay intact.
When it happens
Trigger: Calling internal flow-api-translator functions (flowDefToTSDef, isReactImport path) with a node whose .parent chain is broken: a detached/hand-built node, a Program parsed by flow-parser without scope analysis, or a scopeManager produced from a different parse of the code. Reached during JSX translation in translateFlowDefToTSDef/translateFlowToTSDef.
Common situations: Using the translator's internal modules directly instead of the public translateFlowDefToTSDef API (which parses and analyzes coherently); pre-transforming the AST with a custom pass that drops parent pointers; passing a scopeManager from a stale copy of the code.
Related errors
- Unexpected function parameter ${param.type}
- flowImportTo: Unexpected transform result.
- updatePendingStatements: Variable for dependency "${dep}" no
- updatePendingStatements: Variable parent of "${dep}" not fou
- createTranslationContext: Module scope not found
AI-assisted analysis of facebook/flow@5c86586199 (2026-08-20).
Data as JSON: /api/errors/65a94e8095ca5a53.
Report an issue: GitHub.