Recommended Free Tools
An UnboundLocalError usually does not mean the variable is missing. It means Python has already decided, for the entire function body, that the name is local to that function, and the line that fails reads the name before any value has been bound to that local. The module-level value you can see is never consulted.
What the error means
UnboundLocalError is a subclass of NameError. The built-in exceptions reference (Python 3.12 documentation) defines it as the error raised when a local name is referenced in a function or method but no value has been bound to it at that point. The difference from a plain NameError matters: a plain NameError means Python could not find the name anywhere it looked, while UnboundLocalError means Python found that the name belongs to the function’s local scope and had nothing to give it yet.
Why a later assignment breaks an earlier read
Python decides whether a name is local when it compiles the function, not when the line runs. The Python Language Reference, in the “Resolution of names” section of the execution model (Python 3.14 documentation), states the rule directly: if a name binding operation occurs anywhere within a code block, all uses of the name within that block are treated as references to the current block. Reading a name earlier in the function does not change this.
The Python FAQ (Programming FAQ, Python 3.14 documentation) gives the canonical example:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errors#1 Best Overall
x = 10
def foo():
print(x)
x += 1
Calling foo() raises UnboundLocalError. The augmented assignment x += 1 is a binding operation, so x is local throughout foo. The print(x) on the first line tries to read that local before it has a value, even though the module already bound a global x to 10. A function that only printed x, with no assignment anywhere in its body, would read the module value without trouble.
Which statements make a name local
A name is local to a function if the function binds it anywhere, and binding is broader than a plain =. The execution model enumerates these forms, and each one counts:
Rank #2
- Plain and augmented assignment:
x = …,x += … - Function parameters
- Nested
defandclassstatements, which bind the defined name importstatements, includingimport x as yandfrom m import x- Loop targets in
forstatements and targets inwith … asclauses - Names bound by
except … as(which Python also deletes when the clause ends)
This is why the traceback line is rarely where the fix belongs. The binding that caused the problem may sit several lines below the failing read, or inside a branch that never runs.
A conditional binding produces the same error on the paths that skip it:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
def report(flag):
if flag:
total = 1
return total # UnboundLocalError when flag is false
The name is local because of the assignment inside the if. When flag is false, the return reads an unbound local.
Fixing the error
The right fix depends on which binding the function is meant to use. Make that decision first, then change the code to match.
Update a module-level variable: declare global
If the function is meant to read and rebind the module-level name, add a global declaration before any use of the name in the function. Python will then treat every reference in that function as the module binding. Placing the declaration after the first use is a compile-time SyntaxError, so it belongs at the top of the function.
x = 10
def foo():
global x
print(x) # 10
x += 1 # updates the module-level x
Update an enclosing function’s variable: declare nonlocal
In a nested function, nonlocal x selects the existing binding of x in the nearest enclosing function. The execution model requires that such a binding exist; if no enclosing function binds the name, Python rejects the code at compile time. Use nonlocal only when rebinding the outer variable is the actual intent. For simply reading an enclosing variable, no declaration is needed.
Best Value
Use a local value: bind it before the read
If the function should work with its own variable, give that variable a value before the first read, and make sure every path to the read binds it. For the report example, one fix is to initialize on every path:
def report(flag):
total = 0
if flag:
total = 1
return total
Initializing at the top also removes the error when the code is later restructured, which is a common source of this bug in long functions.
Mutating an object does not rebind the name
Calling a method on an object does not bind the name, so it never triggers this error. The distinction is between changing an object and assigning to the name:
items = []
def add(value):
items.append(value) # works: items is only read
def add_bad(value):
items += [value] # UnboundLocalError: += rebinds items
Both functions appear to change items, but only the second assigns to the name. The first needs no declaration. The second either needs global items or should use items.extend([value]), which mutates the list in place.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →| Intended behavior | Appropriate change |
|---|---|
| Use or rebind a variable local to this function | Bind it before the first read on every path |
| Rebind a module-level variable | Declare global at the top of the function |
| Rebind a variable in an enclosing function | Declare nonlocal in the nested function |
| Change an object’s contents without rebinding the name | Call a mutating method; no declaration needed |
A troubleshooting sequence
- Search the whole function, not just the traceback line, for every binding of the failing name, including imports, loop and
withtargets, and nested definitions. - Decide which binding the code should use: local, module-level, or enclosing-function.
- If the intent is an outer binding, add
globalornonlocalat the top of the function. - If the intent is a local binding, initialize the variable before any read and check each branch that leads to the read.
- Re-run the function along the path that failed. If the error persists, look for a second binding of the same name that you did not expect.
How it differs from related errors
- Plain
NameErrormeans the name was not found in any scope Python searched. Check spelling, imports, and whether the name is defined before use at module level. - Closures normally read free variables from enclosing functions without any declaration. Only rebinding requires
nonlocal. - Class bodies do not act as an enclosing scope for methods. A method cannot read a name defined in the class body as if it were a local of the surrounding code, so a name that looks shared with the class may still be resolved as global or unbound inside the method.
When the traceback names a variable that clearly exists elsewhere in the file, the cause is almost always a binding inside the function itself, and the search in the sequence above will find it.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




