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
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
| Pattern | Why it's reassignment | Fix |
|---|---|---|
const i = 0; i++ | increment rebinds i | declare the counter with let |
const total = 0; total += x | += is shorthand for reassignment | use let total |
const flag = false; flag = true | direct reassignment | use let flag |
arr.push(x) on a const arr | not an error — mutation is allowed | no fix needed |
obj.key = x on a const obj | not an error — mutation is allowed | no fix needed |
Debugging checklist
- ✓ Read the failing line — is it
=/+=/++on the variable itself (reassignment)? - ✓ If yes, and the value genuinely needs to change: switch the declaration to
let - ✓ If you only meant to update a property or array element: that's mutation, already allowed with
const— check you didn't accidentally reassign the whole variable instead - ✓ Loop counters and accumulators almost always need
let, notconst - ✓ Destructured variables are independent bindings — use
let { x } = objifxwill be reassigned
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
- TypeError: invalid assignment to const (MDN)
- const (MDN)
More JavaScript & runtime errors
Browse the full reference for JavaScript, Node.js, and database errors — exact message, cause, and fix.