Hive
fix(cli): graph recursion overflow
GitHub issue · Closed
Hello Tuist team!
Problem
Large valid Tuist dependency graphs can crash tuist generate with a stack overflow when graph-processing code uses recursive traversal.
For most projects this is unlikely to be noticeable because their target graphs are small or shallow. However, we hit this in a sufficiently large generated target graph on Tuist 4.182.0: after a routine project change, the graph shape changed enough to increase traversal depth, and tuist generate started crashing. The graph was valid and acyclic; the crash was caused by recursive graph processing, not by circular dependencies or invalid manifests.
Despite that we originally hit this in our project on Tuist 4.182.0, the same class of issue is still reproducible on current main at the time of validation (4.201.0-canary.9).
What Changed
This PR replaces several recursive graph traversals with iterative implementations:
CircularDependencyLinterGraphCircularDetectortopologicalSort, moved intoTuistSupportModuleMapMapperStaticProductsGraphLinter
The local topologicalSort replaces the implementation imported from TSC. The upstream source, swiftlang/swift-tools-support-core, is already deprecated, so fixing it there would not be useful for Tuist.
The changes keep existing behavior and diagnostics, but use explicit stacks instead of relying on the process call stack.
Why This Helps
The affected code now handles deep dependency paths without growing the Swift call stack per graph node. This makes graph processing stack-safe for large acyclic workspaces while preserving existing caching, ordering, and linting behavior where relevant.
Validation
I created a standalone fixture generator to validate the fix:
igooor-bb/tuist-large-dag-repro
The fixture generates a large valid DAG that is intentionally closer to a real large workspace than a single linked list: multiple projects, feature layers, shared infrastructure, bridge targets with high fan-in, and no dependency cycles.
Primary validation command:
TUIST_BIN=/path/to/tuist ./scripts/reproduce.sh stress --no-focus
Focused-target validation path:
TUIST_BIN=/path/to/tuist ./scripts/reproduce.sh stress --focus
For more details about the fixture structure, available presets, and reproduction commands, see the fixture repository README.
Expected result:
- unpatched binary crashes with stack overflow in one of the recursive graph-processing paths;
- patched binary: generation completes successfully on the same fixture.
Test Coverage
The new long-chain tests in ModuleMapMapper and StaticProductsGraphLinter mostly verify that traversal no longer crashes due to recursion depth. They do not try to fully prove result equivalence on a very long chain.
Existing shorter tests already cover preservation of transitive metadata and warning behavior, so I would not treat this as a blocker. I can still add stronger long-chain result assertions in this PR, or follow up separately with an issue.
Notes
-
The original issue in our project appeared on Tuist 4.182.0 and first surfaced in
CircularDependencyLinter. I could not perfectly reproduce our production graph shape in a standalone fixture, but the fixture became a stronger stress test. After fixing the first crash, it exposed the next recursive risks one by one in later graph-processing stages. -
I could not reproduce a stack overflow specifically through
GraphCircularDetector, but it used recursive graph traversal as well and had the same potential failure mode on sufficiently deep dependency paths.
The fix for graph recursion overflow is now available in 4.201.0-canary.12. Recursive graph traversals in CircularDependencyLinter, GraphCircularDetector, topologicalSort, ModuleMapMapper, and StaticProductsGraphLinter have been replaced with iterative implementations, preventing stack overflow crashes on large dependency graphs. Update to this version to get the fix.
A fix for graph recursion overflow is now available in 4.201.0-canary.19. This replaces recursive graph traversals with iterative implementations in several components, preventing stack overflows on large valid dependency graphs. Update to 4.201.0-canary.19 to get this fix.