UnboundLocalError: local variable referenced before assignment occurs when a function reads a name before that local name has received a value. Python decides which names are local when it compiles the function. If an assignment appears anywhere in the function body, reads of that name are normally treated as reads of the local binding unless the function declares global or nonlocal.
Quick answer
Find the name in the error, then search the same function for an assignment to it. The best repair is usually to pass the value into the function and return the updated value. Use global only for deliberate module-level state, and use nonlocal when a nested function must update a variable in its nearest enclosing function.
The important detail is compile-time scope analysis. A later assignment in a branch can still make the name local for the entire function, even if that branch is not taken during the failing call. The official references for UnboundLocalError, the global statement, the nonlocal statement, and Python scopes describe the rule.

Reproduce the error
The module-level value exists, but the assignment inside the function changes how Python classifies the name.
count = 10
def broken_increment():
print(count)
count = count + 1
return count
try:
broken_increment()
except UnboundLocalError as error:
print(type(error).__name__)
print(error)
Python sees the assignment to count and treats count as local throughout broken_increment(). The first print(count) therefore reads an uninitialized local variable rather than the module-level value.

Return the updated value
The most maintainable fix is to make state flow explicit. Pass the current value in, calculate the next value, and return it to the caller.
def increment(current):
next_count = current + 1
return next_count
count = 10
count = increment(count)
print(count)
This design works for counters, totals, configuration values, and small transformations. It avoids hidden dependencies and makes a function easy to test with one input and one expected output.
Use a local default when state is local
If the function owns the state, initialize it before the first read. This is appropriate when each call should start from a fresh value.
def build_labels(items):
labels = []
for item in items:
labels.append(str(item))
return labels
print(build_labels([1, 2, 3]))
Initialization should match the function’s intended lifecycle. Do not add a default merely to silence the exception if the value is required from the caller. In that case, make it a parameter and validate it at the boundary.

Use global only for deliberate module state
The global statement tells Python that an assignment targets the module-level name. It repairs the narrow example, but it also creates shared mutable state, so callers and tests must account for the change.
count = 10
def increment_global():
global count
count = count + 1
return count
print(increment_global())
print(count)
Use this approach for a genuinely process-wide setting or counter, not as a default response to the error. Shared state can leak between tests, web requests, and repeated calls.
Use nonlocal in a closure
The nonlocal statement applies to a nested function. It updates a name in the nearest enclosing function scope, not the module scope.
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter())
print(counter())
This pattern is useful when a factory intentionally returns a callable with private state. If the nested function only reads the enclosing value, no nonlocal declaration is needed; it is required for assignment.

Check conditional assignments
A branch can leave a local name uninitialized even when the function does not read a module variable. Give every path a value or return before the read.
def label_for(value):
if value is not None:
label = str(value)
else:
label = "unknown"
return label
print(label_for(None))
print(label_for(7))
Use an explicit else, an early return, or a default initialization. The right choice depends on whether the missing case is valid data or an error that should be reported.
Inspect scope while debugging
When the name is not obvious, inspect the function’s local variables and assignments rather than changing unrelated code. The dis module can show whether Python compiled a name as a local, but a small refactor is usually clearer than relying on bytecode details.
def sample():
value = 1
return value
print(sample.__code__.co_varnames)
print(sample())
Use this as a diagnostic aid, not as application logic. The durable fix is to clarify ownership of the value and make the data flow visible to the reader.

Common mistakes
- Reading a module setting and assigning to the same name without
global. - Initializing a variable in only one conditional branch.
- Using
globalwhen a parameter and return value would be simpler. - Using
nonlocaloutside a nested function or when no enclosing binding exists. - Fixing a notebook cell without clearing an old binding.
The practical rule is to decide who owns the state. Pass and return it for ordinary business logic, initialize it when the function owns it, use global for intentional module state, and use nonlocal for intentional closure state. That removes the ambiguity that produces UnboundLocalError.
Make state ownership visible in tests
A test that calls a stateful function more than once can reveal accidental global or closure state. Prefer a fresh fixture or a new object for each test when state should not leak. If shared state is intentional, provide a reset operation or inject the state so the test can control its starting value.
Static analysis tools can also flag names that are assigned and read in confusing paths, but a linter cannot choose the correct ownership model for you. Treat the warning as a prompt to simplify the function’s inputs, outputs, and scope boundaries.
Clear scope boundaries also make refactoring safer: callers can see which values enter a function and which values come back out.
For name-binding diagnostics, compare Python inspect tools with guarded conditional imports. Read python inspect and python conditional import for the related workflow.
Frequently Asked Questions
Frequently Asked Questions
Why does local variable referenced before assignment happen?
Python treats a name as local when the function assigns to it, so a read before that assignment accesses an uninitialized local binding.
What is the best fix for UnboundLocalError?
Usually pass the value into the function and return the updated value. This makes ownership and state flow explicit.
When should I use global?
Use global only when a function intentionally updates module-level state. Otherwise prefer parameters, return values, or an object.
When do I use nonlocal?
Use nonlocal inside a nested function when it must assign to a variable in the nearest enclosing function scope.