ESLint `no-cycle` Plugins Fail to Detect Cycles in Next.js
A critical caching bug in popular ESLint plugins prevents accurate import cycle detection in large codebases like Next.js, impacting code health and reliability. The Answer Up Front Developers…
A critical caching bug in popular ESLint plugins prevents accurate import cycle detection in large codebases like Next.js, impacting code health and reliability.
The Answer Up Front
Developers relying on eslint-plugin-import/no-cycle or import-next/no-cycle for maintaining clean import graphs in large JavaScript/TypeScript projects, especially those using Next.js, should be aware of a significant caching flaw. These tools, despite their widespread adoption, can report zero cycles even when dozens exist, leading to a false sense of security. The underlying issue stems from an interaction between depth-bounded DFS and an overly aggressive nonCyclicFiles cache. For robust cycle detection, consider tools like oxlint or custom solutions that prioritize correctness over potentially flawed caching optimizations. If you ship large-scale JavaScript/TypeScript, this bug means you likely have more cycles than you think.
Methodology
This v0 review draws on the founder Ofri Peretz's published claims and technical analysis at https://dev.to/ofri-peretz/no-cycle-finds-0-cycles-in-nextjs-and-other-lies-caches-tell-you-3ld8, accessed on 2026-05-24. The review covers the algorithmic flaw identified in eslint-plugin-import/no-cycle and import-next/no-cycle, the benchmark results on the Next.js codebase (131K stars, 14,556 source files), and the proposed explanation for the discrepancy. Specifically, the founder reports that both ESLint plugins found 0 cycles in Next.js, while oxlint found 17 cycles. Further testing with a custom rule on a 33-file subset of Next.js's packages/next/src/client/components/router-reducer/** immediately revealed 5+ cycles. This review does not include independent performance benchmarks, long-term workflow integration assessments, or edge-case analyses beyond the specific scenario detailed in the source. Update cadence: re-tested when claims diverge from observed behavior.
What It Does
Import Cycle Detection
Import cycle detection tools analyze a codebase's dependency graph to identify circular imports. These cycles can lead to issues like unexpected runtime behavior, difficult-to-debug errors, and challenges with code splitting or tree-shaking. The core algorithm typically involves a Depth-First Search (DFS) traversal of the import graph, starting from each file and checking if the traversal returns to the starting file.
The Role of Caching
To improve performance, especially on large codebases, these tools employ caching mechanisms. A common strategy, as detailed in the source, involves a nonCyclicFiles cache. This cache stores files that have been previously determined not to be part of any cycle. If a file is in this set, the DFS can skip its traversal, significantly reducing computation from an O(N²·D) naive approach to something more manageable for CI/CD pipelines. The founder reports cache hit rates often exceed 70% in real-world scenarios.
A Flawed Optimization
The specific implementation of the DFS algorithm, combined with this caching, is where the critical bug lies. The DFS includes an early return condition for maxDepth to prevent infinite loops or excessively long traversals. The flaw occurs when a file is marked as nonCyclic due to an early return from a depth-limited DFS. If a cycle exists beyond that maxDepth, the file might be incorrectly cached as acyclic. Subsequent checks, even if they could reach the deeper cycle, would then skip this file due to the cache, perpetually hiding the cycle.
What's Interesting / What's Not
What's most interesting here is the insidious nature of the bug. It's not a simple logic error but a subtle interaction between two seemingly reasonable optimizations: depth-bounded DFS and aggressive caching. The fact that widely used ESLint plugins (eslint-plugin-import/no-cycle and import-next/no-cycle) both exhibit this behavior, and report a clean slate (0 cycles) on a massive project like Next.js, is a stark reminder that even foundational developer tools can harbor critical, long-standing flaws. The founder's discovery, initially sparked by a discrepancy between these ESLint plugins and oxlint (which found 17 cycles), highlights the value of cross-tool validation and independent verification.
What's less interesting is the core cycle detection algorithm itself; it's a well-understood graph traversal problem. The novelty lies entirely in the failure mode of its caching layer. This isn't a new algorithm for cycle detection, but rather a crucial lesson in the reliability of performance optimizations. The bug's location, 60 lines deep in the cache layer, underscores how deeply embedded and hard to spot such issues can be without rigorous testing across varying scopes and depths.
Pricing
eslint-plugin-import/no-cycle, import-next/no-cycle, and oxlint are open-source tools, available at no direct cost. The founder's no-cycle rule, which identified the bug, is also implied to be open-source or a custom implementation.
Pricing snapshot: 2026-05-24
Verdict
The caching bug identified in eslint-plugin-import/no-cycle and import-next/no-cycle renders these tools unreliable for comprehensive import cycle detection in large, complex codebases. The reported discrepancy of 0 cycles found by these plugins versus 17 by oxlint and 5+ by a custom rule on a subset of Next.js demonstrates a critical failure to report existing issues. Developers should not blindly trust the "all clear" from these specific ESLint plugins, especially if they have depth-bounded DFS enabled. Instead, consider oxlint for its reported accuracy or invest in custom tooling with carefully validated caching strategies. The priority for cycle detection must be correctness over potentially misleading performance gains.
What We'd Test Next
Our next steps would involve reproducing the reported caching bug in a controlled environment, creating a minimal reproducible example, and then systematically testing various versions of eslint-plugin-import/no-cycle and import-next/no-cycle to identify when the bug was introduced and if any fixes have been implemented. We would also benchmark oxlint's performance and accuracy across a wider range of open-source projects with known cycle counts. A key test would be to compare the performance impact of disabling the nonCyclicFiles cache in affected tools against the performance of oxlint to quantify the real-world trade-off between speed and correctness.
The investor read
This signal highlights a critical reliability issue in widely adopted developer tooling, specifically in the static analysis space. The fact that eslint-plugin-import/no-cycle and import-next/no-cycle can silently fail on a project the size of Next.js underscores the market opportunity for more robust, correctness-focused alternatives. oxlint's native Rust implementation, which correctly identified cycles, suggests a trend towards higher performance and reliability through lower-level language implementations. Investors should watch for tools that offer verifiable correctness guarantees, especially in foundational areas like code quality and dependency management. Companies building such tools, particularly those with strong benchmarking and a focus on eliminating subtle failure modes, could capture significant market share from existing, less reliable solutions. This also signals that developer spend will increasingly flow towards tools that provide actionable, accurate insights rather than false positives or, worse, false negatives.
Every claim ties to a primary source. See our methodology.