oxc-project/oxc · error · OxcDiagnostic

Dependency cycle detected

Error message

Dependency cycle detected

What it means

Diagnostic from the oxlint rule import/no-cycle (restriction category). It reports that the current module can be reached again by following its own imports: the linter walks the resolved module graph and, when a path returns to the current file, flags the import statement that starts the cycle, attaching a note that prints the full cycle with resolved paths. Cycles are a real hazard — it is common to import an `undefined` binding because the other module has not finished evaluating. Options: `maxDepth` (default unlimited), `ignoreTypes` (default true, type-only imports ignored), `ignoreExternal` (default false), `allowUnsafeDynamicCyclicDependency` (default false).

Source

Thrown at crates/oxc_linter/src/rules/import/no_cycle.rs:25

use cow_utils::CowUtils;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_str::CompactStr;
use rustc_hash::FxHashSet;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    ModuleRecord,
    context::LintContext,
    module_graph_visitor::{ModuleGraphVisitorBuilder, ModuleGraphVisitorEvent, VisitFoldWhile},
    rule::{DefaultRuleConfig, Rule},
};

fn no_cycle_diagnostic(span: Span, stack: &[(CompactStr, PathBuf)], cwd: &Path) -> OxcDiagnostic {
    let cycle_description = format_cycle(stack, cwd);
    OxcDiagnostic::warn("Dependency cycle detected")
        .with_help("Refactor to remove the cycle. Consider extracting shared code into a separate module that both files can import.")
        .with_note(format!("These paths form a cycle:\n{cycle_description}"))
        .with_label(span)
}

fn self_referencing_cycle_diagnostic(span: Span, is_import: bool) -> OxcDiagnostic {
    OxcDiagnostic::warn("Dependency cycle detected")
        .with_help(if is_import {
            "Remove the self-referencing import."
        } else {
            "Remove the self-referencing export and consider using a named export instead."
        })
        .with_label(span.primary_label("this module references itself"))
}

fn format_cycle(stack: &[(CompactStr, PathBuf)], cwd: &Path) -> String {
    let mut lines = Vec::with_capacity(stack.len() * 2 + 1);

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Break the cycle structurally: extract the shared code both modules need into a third module both import
  2. Convert value imports that only carry types to `import type { ... }` — ignoreTypes defaults to true so those no longer count
  3. Defer one direction with a dynamic `await import(...)` at the use site; if acceptable, set `allowUnsafeDynamicCyclicDependency: true`
  4. For noisy node_modules or deep graphs, set `ignoreExternal: true` and/or a bounded `maxDepth`

Example fix

// before (dep-a.js)
import { b } from './dep-b.js';
export function a() { return b(); }
// dep-b.js
import './dep-a.js';
export function b() { return 1; }

// after (dep-b.js no longer imports dep-a.js)
export function b() { return 1; }
Defensive patterns

Strategy: validation

Validate before calling

// independent pre-check with madge (catches cycles oxlint's resolver might miss)
// npx madge --circular --extensions js,ts,tsx src/
// .oxlintrc.json guardrails: { "rules": { "import/no-cycle": ["warn", { "ignoreExternal": true, "maxDepth": 50 }] } }

Prevention

When it happens

Trigger: A imports B and B (directly or transitively) imports A; the diagnostic is emitted on A's first import statement of that chain, with the stack printed via format_cycle. Reported from run_once in crates/oxc_linter/src/rules/import/no_cycle.rs:25 when the module-graph visitor finds resolved_absolute_path equal to the current file.

Common situations: Barrel files (index.ts re-exports siblings that also import from the index); mutual imports created during refactoring (utils imports constants, constants imports utils); type-only cycles that developers expect to be ignored (covered by the default ignoreTypes:true only when `import type` is actually used).

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/cc5b83285e7454e9. Report an issue: GitHub.