UnboundLocalError trying to use a variable supposed to be global that is reassigned even after first use
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
UnboundLocalError appears when Python decides a name is local inside a function, but that local value is read before assignment. This often surprises developers who intended to read a global variable and then update it. Understanding Python scope rules is the fastest way to fix the error correctly.
Core Topic Sections
Why this error happens
Inside a function, any assignment to a variable name makes that name local by default. Python determines this at compile time for the function body.
Example that fails:
Even though the print statement appears before assignment, Python already marked counter as local because assignment exists later in the same function.
Fix 1: use global intentionally
If you really want to mutate a module-level variable, declare it with global.
This works, but global mutation should be limited because it increases coupling and test complexity.
Fix 2: avoid global mutation with return values
A cleaner design is to pass value in and return updated value.
This style is easier to test and reason about in larger codebases.
nonlocal for nested function scopes
When dealing with nested functions, nonlocal targets the nearest enclosing function scope, not module scope.
Use nonlocal only when nested closure state is intentional.
Scope inspection and debugging steps
When debugging scope-related errors:
- Search function body for assignments to the same name.
- Check if the name should be local, nonlocal, or global.
- Rename variables to avoid accidental shadowing.
- Prefer explicit inputs and outputs over hidden shared state.
These steps solve most scope bugs quickly.
Common real-world scenarios
This error appears frequently in:
- Accumulator counters in scripts.
- Caching variables shared across helper functions.
- Flags updated inside callback handlers.
- Notebook cells where variable reuse is inconsistent.
Consistent naming and clear data flow reduce these failures.
Refactor pattern for maintainability
If many functions depend on one mutable value, consider encapsulating state in a class or dataclass.
Object-based state management is explicit and easier to extend than scattered globals.
Testing strategy for scope-sensitive code
Add focused tests that call functions multiple times and validate state transitions. Bugs often appear only after second or third call.
Recommended checks:
- Initial value behavior.
- Repeated update behavior.
- Reset behavior if supported.
- Independence between separate instances.
Scope bugs become predictable when state transitions are tested explicitly.
Common Pitfalls
- Assuming read-before-write of a global name is safe when the function also assigns it.
- Using
globaleverywhere instead of improving function design. - Confusing
globalandnonlocalin nested function code. - Reusing variable names that shadow outer scope unintentionally.
- Debugging only first call and missing state-related failures on later calls.
Summary
UnboundLocalErroris a scope resolution issue, not a runtime random failure.- Assignment inside a function makes the name local unless declared otherwise.
- Use
globalonly when module-level mutation is genuinely required. - Prefer return-value or object-based state flow for maintainable code.
- Test state transitions explicitly to catch scope bugs early.

