Fixing 'UnboundLocalError: local variable referenced before assignment' in Python

beginner🐍 Python2026-05-21| Python 2.x / 3.x β€” all platforms (Linux, macOS, Windows)

Error Message

UnboundLocalError: local variable 'x' referenced before assignment
#python#variable scope#unboundlocalerror#global

The Error

UnboundLocalError: local variable 'x' referenced before assignment

This one bites me every few months. You have a variable that clearly exists, you're reading it inside a function, and Python throws a fit. The confusing part: the variable does exist β€” just not locally. Python decided it should be local before you ever assigned it.

Why Python Throws This

Python determines whether a variable is local or global at compile time, not at runtime. Spot any assignment to a name anywhere in a function body, and Python marks that name as local for the entire function β€” including lines that come before the assignment.

Classic trap:

count = 0

def increment():
    print(count)   # <-- UnboundLocalError here
    count += 1     # Python sees this assignment β†’ marks 'count' as local

increment()

count exists at module scope. Doesn't matter. The count += 1 line is enough to make Python treat the whole function's count as local. So the print(count) above it tries to read a local variable that hasn't been assigned yet. Bang.

Step-by-Step Fix

Fix 1: Use global when modifying a module-level variable

When the variable lives at module scope and you need to modify it inside a function, declare it explicitly:

count = 0

def increment():
    global count      # tell Python: 'count' refers to the module-level variable
    print(count)      # reads module-level count β†’ 0
    count += 1

increment()
print(count)  # 1

One caveat: only use global when you genuinely need to mutate the outer variable. If you're just reading it β€” no assignment anywhere in the function β€” you don't need global at all.

Fix 2: Use nonlocal for nested functions (closures)

Same problem, different scope. When the variable belongs to an enclosing function rather than module scope:

def outer():
    count = 0

    def inner():
        nonlocal count   # refers to outer()'s 'count'
        count += 1
        print(count)

    inner()  # prints 1
    inner()  # prints 2

outer()

nonlocal landed in Python 3. In Python 2, closures couldn't rebind outer names β€” the usual workaround was wrapping the value in a mutable container like a single-element list: count = [0], then count[0] += 1.

Fix 3: Initialize the variable locally first

Sometimes the real fix is simpler β€” you're accidentally re-using a name that shadows an outer variable:

# Broken: tries to read 'result' before it's assigned
def compute(data):
    if data:
        result = process(data)
    return result   # UnboundLocalError if data is falsy

# Fixed: give it a default
def compute(data):
    result = None
    if data:
        result = process(data)
    return result

Fix 4: Restructure to avoid the mutation entirely

Honestly, this is usually the cleanest path. Pass the value in, return the new value, skip globals altogether:

count = 0

def increment(n):
    return n + 1

count = increment(count)
print(count)  # 1

Pure functions are easier to test. They also make this entire category of bug impossible.

Less Obvious Cases

Conditional assignment

x = 10

def maybe_assign(flag):
    if flag:
        x = 99      # Python marks 'x' as local for the whole function
    print(x)        # UnboundLocalError when flag is False

maybe_assign(False)

Two options: initialize x at the top of the function body, or declare global x. Either works; the initializer approach is usually cleaner.

Augmented assignment (+=, -=, etc.)

count += 1 is syntactic sugar for count = count + 1. The assignment on the left makes Python classify count as local. The right side then tries to read that (not-yet-assigned) local. This is the single most common trigger for this error β€” worth burning into memory.

Inside try/except blocks

def load():
    try:
        data = fetch()
    except Exception:
        pass
    return data   # UnboundLocalError if fetch() raised

When fetch() raises, the assignment never happens and data stays unbound. Fix: add data = None before the try block.

Verify the Fix

Run the function directly with the input that originally triggered the error:

python your_script.py

# or in a REPL:
>>> increment()
0
>>> print(count)
1

No UnboundLocalError and the return value looks right? You're done.

For the conditional-assignment case, test both branches β€” flag=True and flag=False. The error only appears on one path, so a single passing test isn't enough.

Quick Cheat Sheet

  • Modifying a module-level variable inside a function β†’ add global varname
  • Modifying an enclosing function's variable β†’ add nonlocal varname
  • Variable only assigned in one branch of an if/try β†’ initialize before the branch
  • Just reading, not assigning β†’ no keyword needed; Python can already see the outer scope
  • Best long-term fix β†’ avoid mutable globals; pass values as parameters and return results

Related Error Notes