If reading and incrementing a counter outside a function raises UnboundLocalError, Python has treated the same name as a different, local variable. By default, a name assigned inside a function is local to that function.
count = 0
def increment():
count += 1 # Tries to read local count before assigning it.Calling increment() fails when the right-hand side of count += 1 tries to read the local count first. Even though a variable with that name exists outside the function, the assignment makes the name local inside it. The first diagnostic step is to trace where count is assigned on the failing line.
Returning state is easier to test
Inside the function, count += 1 attempts to read an uninitialized local count and fails. A function that accepts a value and returns one more works instead. The outer count remains 0 unless the caller assigns the result back to it.
count = 0
def increment(count: int) -> int:
return count + 1
count = increment(count)
assert count == 1
assert increment(4) == 5Making input and output explicit removes a hidden dependency on external state. It also reduces the need to reset a global variable for every test.
If you put the broken and corrected examples in one file, redefine the function before testing the correction. To verify the failure, call the original function and check for UnboundLocalError; test the corrected function separately on both paths.
global should be a last resort
If you truly need to change module-level state, you can declare global count, but changes from multiple call paths become harder to trace. Encapsulating state in an object or letting the caller own it is usually safer. Changing a variable from an enclosing function uses nonlocal, which has a different purpose.
In a server where multiple request flows may touch state, a module-level counter cannot substitute for durable shared storage. Two processes have two counters, and a restart loses both. Keep simple calculations in arguments and return values. If shared state is required, define its owner and synchronization method separately.
Reproduce the scope error and verify the fix
count = 0
def broken():
count += 1 # UnboundLocalError: local count is not initialized.
try:
broken()
except UnboundLocalError:
pass # Confirmed the failure path.
else:
raise AssertionError("Expected a local-variable error")
def increment(value: int) -> int:
return value + 1
assert increment(count) == 1
assert count == 0 # The outer state has not changed.The first branch confirms that the error occurs; the second checks the returned value and that outer count stays unchanged. Adding global merely to silence the error can leave a state problem whose result varies with call order.
Key takeaways
Assignment inside a function makes a name local by default. Instead of adding global to modify outside values, accept an argument and return the new value so data flow is visible. A scope error is often a question of who owns state, not just a problem with a variable name.

