facebook/flow · error · Error
Type size limit exceeded
Error message
Type size limit exceeded
What it means
This error is thrown by the type normalizer when the normalization of a type visits more type nodes than the configured budget (max_type_size) allows. State::consume_type_node decrements a counter for every type node visited; when the counter is already 0, it fails with ErrorKind::TypeSizeLimit and message "Type size limit exceeded". The limit exists to guard against pathologically large or recursive types blowing up normalization time/memory.
Solutions
- Increase the configured maximum type size passed to reset_type_size_budget (raise the budget for the analysis run).
- Check the type being normalized for accidental blowup (recursive alias expansion, excessively large unions/tuples) and simplify it in the source program.
- If the limit is intentional, treat this error as a signal to bail out of normalizing that type and fall back to a normalized-type approximation or report a user-facing type-too-complex diagnostic.
- Verify the budget is not being reset/decremented across unrelated types, which would unfairly deplete it for later types.
Example fix
// before state.reset_type_size_budget(Some(1024)); // after state.reset_type_size_budget(Some(4096));
Defensive patterns
Strategy: try-catch
Validate before calling
// estimate node count before normalizing and skip if over budget
fn within_budget(ty: &Ty, budget: usize) -> bool {
ty.node_count() <= budget
} Type guard
fn is_type_size_limit(e: &Error) -> bool {
e.kind() == ErrorKind::TypeSizeLimit
} Try / catch
match state.normalize(ty) {
Ok(n) => n,
Err(e) if e.kind() == ErrorKind::TypeSizeLimit => {
// fall back: use un-normalized type or emit "type too complex" diagnostic
fallback_representation(ty)
}
Err(e) => return Err(e),
} Prevention
- Set the type-size budget generously relative to the largest types in your codebase.
- Measure/estimate type node counts offline and pick a budget with headroom.
- Simplify or cap recursive type aliases and huge unions at the language level.
- Handle TypeSizeLimit explicitly so one oversized type does not abort the whole analysis.
When it happens
Trigger: Normalizing a type whose AST has more nodes than the remaining budget: a State with remaining_type_size = Some(n) where consuming more than n nodes is required. This happens when reset_type_size_budget was called with a small max, or the type being normalized is genuinely huge/deeply recursive.
Common situations: Very large generated or deeply nested types (e.g. huge trait objects, long tuple/union chains), mutually recursive type aliases expanded during normalization, or a max type size configured too low for the codebase being type-checked.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- ; additionally failed to stop Flow
- cached canonicalize failure
- convert_to_server_uris: invalid file url
- Daemon::flush failed
- Daemon::from_channel: bincode deserialize
AI-assisted analysis of facebook/flow@21c11b2ead (2026-09-16).
Data as JSON: /api/errors/3e2c5ed29d7105bd.
Report an issue: GitHub.
Appendix: source
Thrown at rust_port/crates/flow_typing_ty_normalizer/src/normalizer.rs:355
rec_tvar_ids: BTreeSet::new(),
rec_eval_ids: BTreeSet::new(),
found_computed_type: false,
remaining_type_size: None,
}
}
pub fn found_computed_type(&self) -> bool {
self.found_computed_type
}
fn reset_type_size_budget(&mut self, max_type_size: Option<usize>) {
self.remaining_type_size = max_type_size;
}
fn consume_type_node(&mut self) -> Result<(), Error> {
match self.remaining_type_size {
None => Ok(()),
Some(0) => Err(Error::new(
ErrorKind::TypeSizeLimit,
String::from("Type size limit exceeded"),
)),
Some(remaining) => {
self.remaining_type_size = Some(remaining - 1);
Ok(())
}
}
}
}
// Each run of the monad gets assigned its own id.
thread_local! {
static RUN_ID: Cell<i32> = const { Cell::new(0) };
}
// Wrapper around 'run' that assigns a distinct id to every run of the monad.
fn run<T, F>(state: &mut State, f: F) -> Result<T, Error>View on GitHub (pinned to 21c11b2ead)