oxc-project/oxc · warning · OxcDiagnostic

Invalid loop. Its body allows only one iteration.

Error message

Invalid loop. Its body allows only one iteration.

What it means

Diagnostic from oxlint's no-unreachable-loop rule. It reports loops whose body always exits (break/return/throw) on every path of the first iteration, so the loop can never complete a second iteration; the loop construct is pointless or the unconditional exit is a bug. It uses the shared CFG helper effective_unreachable_blocks.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_unreachable_loop.rs:30

};
use oxc_diagnostics::OxcDiagnostic;
use oxc_ecmascript::{
    GlobalContext,
    side_effects::{MayHaveSideEffects, MayHaveSideEffectsContext, PropertyReadSideEffects},
};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{IsGlobalReference, NodeId};
use oxc_span::{GetSpan, Span};

use crate::{
    AstNode,
    context::LintContext,
    rule::{DefaultRuleConfig, Rule},
    utils::effective_unreachable_blocks,
};

fn no_unreachable_loop_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Invalid loop. Its body allows only one iteration.")
        .with_help("Remove the loop or make at least one path continue to the next iteration.")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
struct NoUnreachableLoopConfig {
    ignore: Vec<LoopType>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)]
enum LoopType {
    #[serde(rename = "WhileStatement")]
    While,
    #[serde(rename = "DoWhileStatement")]
    DoWhile,
    #[serde(rename = "ForStatement")]
    For,

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Remove the loop wrapper and keep the body once if single-pass behavior is intended.
  2. Make the break/return/throw conditional so at least one path continues to the next iteration.
  3. Remove the stray break/return left over from debugging.
  4. Add the intentional loop kind to the rule's ignore option (e.g. "WhileLoop", "ForOfLoop", "ForLoop", "ForInLoop", "DoWhileLoop").

Example fix

// before
for (const item of items) {
  handle(item);
  break;
}
// after
for (const item of items) {
  if (!shouldHandle(item)) break;
  handle(item);
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: for (const x of xs) { break; }, while (cond) { return; }, loop bodies that unconditionally throw, or a labeled break that always fires on the first pass.

Common situations: A conditional if (x) break; is refactored into an unconditional break; a debugging break is left in; a loop is kept as a scoping wrapper after the body became single-pass; generated code wraps single-item iteration.

Related errors


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