BugHunt

Scope and closures: variables that are not the variable you meant

Every callback reports the last value, a counter never increments, UnboundLocalError on a variable you can see.

15 free challenges7 Python8 JavaScript

What is scope and closure bug?

The name you wrote resolves to a different variable than the one you meant — a shadowed copy, a shared binding, or one that does not exist yet at the moment the line runs.

Why it happens

Closures capture variables, not values, so callbacks created in a loop can share one binding and all see the final value. Assigning to a name anywhere in a Python function makes it local for the whole function, even before the assignment. Re-declaring a name inside a callback shadows the outer one, so updates land on the copy. And in JavaScript `this` is decided by how a function is called, not where it was written.

How to recognise it

  • Every item in a loop behaves like the last one.
  • A counter is visibly incremented but stays at its initial value.
  • Python reports a variable as local when it is clearly defined above.
  • A method works when called normally and breaks when passed as a callback.

Errors and symptoms this causes

How to fix it

Do not re-declare a name you meant to update from an enclosing scope. Capture per-iteration values explicitly — a default argument in Python, let rather than var in JavaScript. Keep a method attached to its object when passing it somewhere. Declare before use, always.

Practise scope and closure bugs

Working code with one bug in it. Find it, fix it in the browser, and see the explanation. No account needed.

pythonEasy
Function crashing when no match found

last_even(nums) should return the last even number, or -1 if there are none. last_even([1, 3]) should return -1.

Scope error

pythonHard
Different users sharing the same cart

make_baskets(first, second) should start a separate basket for each item, so make_baskets("apple", "bread") gives [["apple"], ["bread"]].

Scope error

pythonHard
Independent objects sharing data

Each Basket should have its own items, so after adding one thing to a fresh basket, two_baskets('x', 'y') returns 1.

Scope error

javascriptHard
Event listeners logging identical values

collectIndexes(n) should schedule one callback per index and resolve to the list of indexes, so collectIndexes(3) gives [0, 1, 2].

Scope error

javascriptHard
Multiple instances incrementing same value

Each tracker from makeTracker() should start at zero, so trackTwice(5, 3) returns 3 — the second tracker's own total.

Scope error

javascriptHard
Class method throwing undefined error

countAll(items) should count how many items were passed in, so countAll([1, 2, 3]) returns 3.

Scope error

pythonHard
The loop variable that changed

make_multipliers() returns a list of 3 functions multiplying by 0, 1, and 2. test_multipliers() calls each with 10 and returns the results.

Scope error

pythonHard
Generated functions returning identical results

make_multipliers([2, 3, 4]) should build one multiplier per factor, so the FIRST one doubles: apply_first([2, 3, 4], 10) returns 20.

Scope error

javascriptHard
Variable returning unexpected values

describe(useLocal) should return "local" when useLocal is true and the outer "global" when it is false.

Scope error

javascriptMedium
Counter stuck at zero

countMatches(words, target) should count how many times target appears, so countMatches(["a","b","a"], "a") returns 2.

Scope error

pythonMedium
The list that remembers

append_to_list(val) should create a new list with val if no list is provided. test_append() calls it twice with 1 and 2.

Scope error

javascriptMedium
Variable not found in scope

finalPrice(price, isMember) should apply a 10% discount for members and return the price to pay.

Scope error

javascriptMedium
ReferenceError crashing the app

priceWithFee(base) should add a flat fee of 5 to the base price, so priceWithFee(10) returns 15.

Scope error

pythonMedium
Function crashing on variable update

add_all(nums) should return the sum of the numbers, so add_all([1, 2, 3]) returns 6.

Scope error

javascriptMedium
Function crashing on return statement

findFirstNegative(nums) should return the first negative number in the array, or null if there isn't one.

Scope error

Stuck on your own code?

Paste it into the visualizer and watch it run line by line, with every variable at every step. Free, and it runs in your browser.

Other bug patterns