TypeError: Assignment to constant variable

Quick answer

You tried to reassign a variable declared with const. const locks the binding (the name) to its initial value permanently — it does not lock the value's contents. If you need to change what the variable points to, declare it with let instead; if you only need to change an object's property or push to an array, that's mutation, which const already allows.

The exact error string

const count = 0;
count = count + 1;
// Uncaught TypeError: Assignment to constant variable.

const total = 0;
total += 10;
// Uncaught TypeError: Assignment to constant variable.

What it actually looks like

Chrome DevTools console showing a red error row reading Uncaught TypeError: Assignment to constant variable., sourced to app.js line 3.
Captured from Chrome 152. The trailing full stop is genuinely part of V8's message — most V8 errors have none, so searching with it included is what matches. Note too that the error fires on reassignment, not on mutating the object a const points at.

Reassignment vs mutation

This is the single most important distinction for this error: const forbids pointing the variable at a new value (reassignment) but says nothing about changing an object or array's existing contents (mutation).

const arr = [1, 2, 3];
arr.push(4);        // ✅ mutation — arr still points to the same array
arr[0] = 99;         // ✅ mutation — same array, different contents
arr = [5, 6];        // ❌ reassignment — Assignment to constant variable

const user = { name: "Ada" };
user.name = "Grace"; // ✅ mutation — same object, updated property
user = { name: "X" };   // ❌ reassignment

Cause 1: a loop counter declared const

for (const i = 0; i < 10; i++) {
  // ❌ i++ reassigns i on the first iteration
}

for (let i = 0; i < 10; i++) {
  // ✅ i needs to change each iteration — use let
}

Cause 2: a running total or accumulator

const total = 0;
for (const price of prices) {
  total += price;   // ❌ total needs to be reassigned each pass
}

let total = 0;         // ✅
for (const price of prices) {
  total += price;       // fine — `price` itself stays const per iteration
}

Cause 3: a flag that toggles

const isOpen = false;
function toggle() {
  isOpen = !isOpen;   // ❌ reassignment
}

let isOpen = false;     // ✅ this value is meant to change over time
function toggle() {
  isOpen = !isOpen;
}

Cause 4: destructured bindings are const too

const { page } = query;
page = page + 1;   // ❌ `page` is its own const binding, regardless of `query`

let { page } = query;   // ✅ if you intend to reassign it
page = page + 1;

Where const genuinely surprises people: loops

The rule "const forbids reassignment" has one consequence that catches almost everyone, because two loops that look equivalent behave differently:

for (const item of items) { ... }     // ✅ fine — every iteration gets a NEW binding
for (const key in object)  { ... }    // ✅ fine — same reason

for (const i = 0; i < 3; i++) { ... } // ❌ TypeError: Assignment to constant variable.

The difference is not a special case for for...of; it follows from what each loop does. for...of and for...in create a fresh binding on every pass, so nothing is ever reassigned. The three-clause for loop creates the binding once and then runs i++ against it, which is a reassignment — and so it throws on the very first increment. Use let there; const is correct and idiomatic in the other two.

This also explains a common misreading of closure advice: because for...of gives each iteration its own binding, a callback created inside the loop captures that iteration's value rather than the final one. That is the same mechanism, not a separate feature.

Not this error: read-only properties and frozen objects

Reassigning a const and writing to a frozen object both look like "you can't change that", but they are different errors with different messages — and mistaking one for the other sends you to the wrong fix:

const config = { retries: 3 };
config = { retries: 5 };     // ❌ TypeError: Assignment to constant variable.
config.retries = 5;          // ✅ allowed — const protects the binding, not the contents

const frozen = Object.freeze({ retries: 3 });
frozen.retries = 5;
// ❌ (strict mode) TypeError: Cannot assign to read only property 'retries' of object '#<Object>'
// ⚠️ (sloppy mode) fails SILENTLY — the write is simply discarded

Verified on Node v24.15.0. Two things worth carrying away. If your message says read only property rather than constant variable, the problem is Object.freeze() somewhere upstream, not your declaration. And in sloppy mode a frozen write neither throws nor takes effect — which is precisely the kind of silent failure that ES modules avoid, since they are always strict.

Common variants at a glance

PatternWhy it's reassignmentFix
const i = 0; i++increment rebinds ideclare the counter with let
const total = 0; total += x+= is shorthand for reassignmentuse let total
const flag = false; flag = truedirect reassignmentuse let flag
arr.push(x) on a const arrnot an error — mutation is allowedno fix needed
obj.key = x on a const objnot an error — mutation is allowedno fix needed

Debugging checklist

Frequently Asked Questions

What does 'Assignment to constant variable' mean?

You used = (or +=, -=, ++, etc.) on a variable declared with const, which only allows a single assignment at declaration. Any later attempt to rebind that name to a new value throws this TypeError, whether the new value is the same type or not.

Can I change the contents of a const object or array?

Yes. const only locks the binding (the variable name), not the value's contents. const arr = [1,2]; arr.push(3) works fine — you're mutating the array, not reassigning arr. Only arr = [4,5] (pointing the name at a different array) throws.

Why does my for loop throw this?

for (const i = 0; i < 10; i++) declares i as const, and the loop's own i++ step tries to reassign it on the very first iteration. Loop counters that increment need let, not const — reserve const for the loop body's per-iteration values that don't change.

Why does a counter or accumulator variable throw this?

Any variable you plan to reassign — a running total, a counter, a flag that flips — must be declared with let. const communicates "this binding will never change"; if your code changes it, that's a signal the declaration should be let, not a bug to work around.

Does destructuring into a const also lock the fields?

const { a, b } = obj creates two new const bindings, a and b — reassigning either later throws the same error, even though obj itself might be mutable. The rule is per-binding, not inherited from where the value came from.

How do I fix this quickly?

If the variable's value genuinely needs to change over time, change its declaration from const to let. If you only meant to update a property or push to an array (not rebind the variable itself), use the mutating operation instead of assignment — the const declaration is already correct.

References

More JavaScript & runtime errors

Browse the full reference for JavaScript, Node.js, and database errors — exact message, cause, and fix.

All Error References Cannot access 'X' before initialization HTTP Status Codes
About the author

Pasindu Ishan is a Senior Software Engineer working on data streaming systems, which means most days end at a JSON payload that will not parse. The JavaScript references here are checked against a real runtime before being written up. He builds the free in-browser tools at JSON Dev Tools.