UnboundLocalError (“local variable referenced before assignment”) means a function reads a variable that Python has decided is local, before that local variable has been given a value. It almost always happens because the function assigns to a name that also exists outside it.
visits = 0
def record_visit():
visits += 1 # UnboundLocalError
# fix: pass it in and return it, or declare it global
def record_visit(visits):
return visits + 1
Newer versions of Python, including the Python 3.12.5 I used for every screenshot here, word the same error as “cannot access local variable ‘visits’ where it is not associated with a value”.
Both messages mean the same thing. Below you’ll see why Python behaves this way, the five situations that trigger it, and which fix to pick.
What the UnboundLocalError traceback looks like
Here’s the classic version: a counter at the top of the file, and a function that adds one to it.
visits = 0
def record_visit():
visits += 1 # reads visits AND assigns to it
return visits
record_visit()
Traceback:
Traceback (most recent call last):
File "C:\pyguides\unbound_error.py", line 7, in <module>
record_visit()
File "C:\pyguides\unbound_error.py", line 4, in record_visit
visits += 1 # reads visits AND assigns to it
^^^^^^
UnboundLocalError: cannot access local variable 'visits' where it is not associated with a value
visits += 1, the line that reads the variable.It looks like it should work. You can see visits right there, set to 0. So why does Python tell you it has no value?
Why does Python treat the variable as local?
Python decides which names are local when it compiles the function, not while it runs it. If a name is assigned anywhere in the function, it’s local everywhere in that function, even on the lines above the assignment.
visits += 1 is an assignment, so visits became local. Then it tried to read the local visits to add one, and there wasn’t one yet.
You can watch this happen. In the next example, the print() comes first, and it still fails because of the line after it:
price = 19.99
def show_price():
print(price) # looks like it reads the global...
price = 24.99 # ...but this line makes price local for the WHOLE function
try:
show_price()
except UnboundLocalError as err:
print("UnboundLocalError:", err)
def only_reads():
return price # no assignment here, so the global is used
print("only_reads():", only_reads())
print("local names in show_price:", show_price.__code__.co_varnames)
print("local names in only_reads:", only_reads.__code__.co_varnames)
Output:
UnboundLocalError: cannot access local variable 'price' where it is not associated with a value
only_reads(): 19.99
local names in show_price: ('price',)
local names in only_reads: ()
co_varnames lists the function’s local names: price is local in one function and absent from the other.A function that only reads price finds the global one with no trouble. The assignment is what changes the rules, which is also why this is a different error from NameError: name is not defined.
Fix 1: pass the value in and return it
This is the fix I’d reach for first. The function gets what it needs as an argument and hands the new value back, so it doesn’t depend on anything outside itself:
def record_visit(visits):
return visits + 1 # take the value in, hand the new value back
visits = 0
visits = record_visit(visits)
visits = record_visit(visits)
print("visits:", visits)
Output:
visits: 2
It’s a bit more typing, but you get a function that’s easy to test and can’t quietly change something elsewhere in your program.
Fix 2: make the variable global
If the function genuinely should change a module-level variable, say so with global:
visits = 0
def record_visit():
global visits # "visits" now means the module-level name
visits += 1
record_visit()
record_visit()
print("visits:", visits)
Output:
visits: 2
global visits tells Python not to create a local. It’s fine for a small script, but in a bigger program, globals make it hard to tell who changed what. Setting global variables in functions covers the details.
Fix 3: use nonlocal in a nested function
When the outer variable belongs to an enclosing function rather than the module, global is the wrong keyword. Use nonlocal:
def make_counter():
count = 0
def tick():
nonlocal count # use the enclosing function's count
count += 1
return count
return tick
tick = make_counter()
tick()
tick()
print("ticks:", tick())
Output:
ticks: 3
Without nonlocal, count += 1 inside tick() raises the same UnboundLocalError, because the assignment makes count local to tick.
UnboundLocalError when a variable is assigned only inside an if
This version has nothing to do with globals. The variable only gets a value on one branch, so the other branch reaches the return with nothing assigned:
def shipping_cost(order_total):
if order_total < 50:
cost = 7.99
return cost # never assigned when the order is $50 or more
print("$20 order:", shipping_cost(20))
try:
print("$80 order:", shipping_cost(80))
except UnboundLocalError as err:
print("UnboundLocalError:", err)
def shipping_cost_fixed(order_total):
cost = 0.00 # a value on every path
if order_total < 50:
cost = 7.99
return cost
print("$80 order, fixed:", shipping_cost_fixed(80))
Output:
$20 order: 7.99
UnboundLocalError: cannot access local variable 'cost' where it is not associated with a value
$80 order, fixed: 0.0
if, so cost never exists.Give the variable a value before the if, or add an else that assigns it. Either way, make sure every path through your function sets it.
UnboundLocalError after an except block
Here’s one that surprises experienced developers. The name you give an exception with as err is deleted when the except block ends:
def parse_price(text):
try:
return float(text)
except ValueError as err:
pass
print("could not parse:", err) # err was deleted when the except block ended
try:
parse_price("$19.99")
except UnboundLocalError as e:
print("UnboundLocalError:", e)
def parse_price_fixed(text):
try:
return float(text)
except ValueError as err:
problem = err # keep it under another name
print("could not parse:", problem)
parse_price_fixed("$19.99")
Output:
UnboundLocalError: cannot access local variable 'err' where it is not associated with a value
could not parse: could not convert string to float: '$19.99'
err existed inside the except block and was gone one line later.Python does this on purpose, so your program doesn’t keep the whole traceback alive in memory. If you need the exception later, copy it to another name inside the block.
Catching UnboundLocalError with except NameError
UnboundLocalError is a subclass of NameError, so an except NameError handler catches both:
print("UnboundLocalError is a NameError:", issubclass(UnboundLocalError, NameError))
total = 0
def add(amount):
total += amount
try:
add(5)
except NameError as err: # catches UnboundLocalError too
print(type(err).__name__, "->", err)
Output:
UnboundLocalError is a NameError: True
UnboundLocalError -> cannot access local variable 'total' where it is not associated with a value
That’s useful to know when you read other people’s error handling, but I wouldn’t catch this error in real code. It’s a bug in the function, and the fix belongs in the function.
Which UnboundLocalError fix should you use?
| Situation | Fix |
|---|---|
| A function updates a module-level counter or total | Pass it in and return it; use global only in small scripts |
| A nested function updates the outer function’s variable | nonlocal name |
The variable is set inside an if, for or try only | Give it a value before the block |
You use an exception after its except block | Copy it to another name inside the block |
| You meant to read the global, not create a local | Rename the local variable |
Keep going with these related Python guides:
- NameError: name is not defined in Python
- Python variable scope: local, global, nonlocal and LEGB
- Set global variables in Python functions
- Access variables outside a function
- Check if a variable exists in Python
Frequently asked questions
What does ‘local variable referenced before assignment’ mean?
The function assigns to a variable, which makes it local, but reads it before that assignment has run. Python raises UnboundLocalError instead of falling back to a global with the same name.
Why does += cause UnboundLocalError?
x += 1 both reads and assigns x. The assignment makes x local to the function, so the read finds an empty local variable.
How do I fix UnboundLocalError?
Pass the value in as an argument and return the result, declare it with global or nonlocal, or make sure every path assigns the variable before it’s used.
What’s the difference between UnboundLocalError and NameError?
NameError means the name doesn’t exist anywhere Python looks. UnboundLocalError means it’s a local name that hasn’t been assigned yet. UnboundLocalError is a subclass of NameError.
Why does my error say ‘cannot access local variable’ instead?
That’s the wording in newer versions of Python. It’s the same UnboundLocalError as ‘local variable referenced before assignment’.
Is using global a bad fix?
It works, and it’s fine in short scripts. In larger programs, passing values in and returning them keeps functions easier to test and reason about. The Python FAQ explains the rule behind this error.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn