oxc-project/oxc · critical

Unexpected re-assignment of `const` variable {name}.

Error message

Unexpected re-assignment of `const` variable {name}.

What it means

Diagnostic from the oxlint rule `no-const-assign` (crates/oxc_linter/src/rules/eslint/no_const_assign.rs). It fires when a variable declared with `const` is later the target of an assignment (including compound assignment, increment/decrement, and for-in/for-of assignment targets). The diagnostic is two-label: `'{name} is declared here as const.'` plus `'{name} is re-assigned here.'`. At runtime this throws `TypeError: Assignment to constant variable`, so the rule is correctness-critical.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_const_assign.rs:11

use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_ecmascript::BoundNames;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{AstNode, SymbolId};
use oxc_span::Span;

use crate::{ast_util::variable_declaration_kind, context::LintContext, rule::Rule};

fn no_const_assign_diagnostic(name: &str, decl_span: Span, assign_span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!("Unexpected re-assignment of `const` variable {name}."))
        .with_help("Use `let` instead of `const` if you need to reassign this variable.")
        .with_labels([
            decl_span.label(format!("{name} is declared here as `const`.")),
            assign_span.label(format!("{name} is re-assigned here.")),
        ])
}

#[derive(Debug, Default, Clone)]
pub struct NoConstAssign;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallow reassigning `const` variables.
    ///
    /// ### Why is this bad?
    ///
    /// We cannot modify variables that are declared using the `const` keyword,

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Change the declaration from `const` to `let` if reassignment is intended: `let x = 1; x = 2;`.
  2. If immutability is intended, change the reassignment into a mutation (for objects/arrays: `items.push(...)` instead of `items = [...]`) or use a new binding.
  3. For accumulators, restructure with `reduce` or a mutable local so the const stays const.
  4. Never widen to `var` — that hides rather than fixes the mutation.

Example fix

// before
const total = 0;
for (const n of nums) {
  total += n; // TypeError: Assignment to constant variable
}

// after
let total = 0;
for (const n of nums) {
  total += n;
}
Defensive patterns

Strategy: validation

Validate before calling

// Prefer the AST-based rule; textual heuristic for hooks:
const decls = [...src.matchAll(/\bconst\s+(\w+)/g)].map(m => m[1]);
const reassigned = decls.filter(n => new RegExp(`\\b${n}\\s*(=[^=]|\\+\+|--|\\*=|\\+=)`).test(src));

Try / catch

// Wrap risky third-party-driven mutation if you cannot lint it:
try { moduleHotReload(); } catch (e) {
  if (e instanceof TypeError && /Assignment to constant variable/.test(e.message)) {
    reportConstReassignment(e);
  } else throw e;
}

Prevention

When it happens

Trigger: An AssignmentExpression / UpdateExpression / ForIn-Of target Reference whose SymbolId resolves to a declaration with `const` kind (checked via `variable_declaration_kind` over bound names) — e.g. `const x = 1; x = 2;`, `x += 1;`, `x++;`, `for (x of ys)` when x is const.

Common situations: Refactoring `let` to `const` for 'prefer-const' and missing a later mutation; adding retry/reset logic (`count = 0`) to a const accumulator; destructured `const {a} = obj` later reassigned; this always hard-crashes in strict-mode ESM, so the lint report precedes a real runtime TypeError.

Related errors


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