facebook/flow · error · Error
sourceType option must be "script", "module", or "unambiguou
Error message
sourceType option must be "script", "module", or "unambiguous" if set
What it means
The second options check in the Flow parser: when sourceType is provided it must be 'script', 'module', or 'unambiguous' ('unambiguous' is normalized away and auto-detected from file contents). Any other string throws.
Source
Thrown at packages/flow-parser/oxidized-src/index.js:52
// results — earlier versions wrote normalized fields back onto `opts`,
// poisoning subsequent calls.
const options: ParserOptions = {...DEFAULTS, ...(opts ?? {})};
// Default to detecting whether to parse Flow syntax by the presence
// of an @flow pragma.
if (options.flow !== 'all' && options.flow !== 'detect') {
throw new Error('flow option must be "all" or "detect"');
}
if (options.sourceType === 'unambiguous') {
// Clear source type so that it will be detected from the contents of the file
delete options.sourceType;
} else if (
options.sourceType != null &&
options.sourceType !== 'script' &&
options.sourceType !== 'module'
) {
throw new Error(
'sourceType option must be "script", "module", or "unambiguous" if set',
);
}
if (options.enableExperimentalComponentSyntax == null) {
options.enableExperimentalComponentSyntax = true; // Enable by default
}
if (options.enableExperimentalFlowMatchSyntax == null) {
options.enableExperimentalFlowMatchSyntax = true; // Enable by default
}
// Record syntax: upstream defaults `enableExperimentalFlowRecordSyntax` to
// `true`. The fork's WASM cwrap (FlowParser.js) reads `options.enableRecords`
// — a separate, OCaml-aligned key intentionally mirroring
// `default_parse_options`. To preserve the upstream-compatible API surface
// on the outside while keeping the OCaml-aligned key on the WASM boundary,
// bridge: default the upstream key, then mirror it onto the cwrap key. TheView on GitHub (pinned to d1341dac89)
Solutions
- Use 'module' for ESM, 'script' for CommonJS/global scripts, 'unambiguous' to auto-detect, or omit the option
- Build parser options explicitly instead of reusing config objects from tsconfig/Babel
Example fix
// before
parse(source, { sourceType: 'commonjs' });
// after
parse(source, { sourceType: 'script' }); // CommonJS files are scripts Defensive patterns
Strategy: validation
Validate before calling
const SOURCE_TYPES = new Set(['script', 'module', 'unambiguous']);
function validSourceType(opts: {sourceType?: unknown}): boolean {
return opts?.sourceType == null || SOURCE_TYPES.has(opts.sourceType as string);
} Type guard
function isSourceType(
v: unknown,
): v is 'script' | 'module' | 'unambiguous' | undefined {
return v == null || v === 'script' || v === 'module' || v === 'unambiguous';
} Try / catch
try {
parse(source, opts);
} catch (err) {
if (err.message.includes('sourceType option must be')) {
const {sourceType, ...rest} = opts; // drop invalid value, use default
parse(source, rest);
} else throw err;
} Prevention
- Map tsconfig module values to parser sourceTypes explicitly ('commonjs' → 'script')
- Build parser options in one place, not by reusing foreign config objects
When it happens
Trigger: Calling parse(code, {sourceType: 'commonjs'}) or 'esm'/'es6' — none are accepted; feeding values in from other tools' configs.
Common situations: Porting options from Babel or other parsers; passing tsconfig's `module: "commonjs"` value straight into parser options; typos like 'modules'.
Related errors
- flow option must be "all" or "detect"
- Parser out of memory
- SimpleTransform: invalid array result for root node
- SimpleTransform.transformProgram: Expected program node.
- Expected parent node to be set on "${target.type}"
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/d582e00971798fc1.
Report an issue: GitHub.