LookupError

One except clause for both "no such key" and "no such index" — ideal for digging through nested JSON-like data.

InheritsBaseException›Exception›LookupError
Base classPython 3 (all)Live demo
LookupError(*args)
Raised by
d[key] (KeyError), seq[i] (IndexError), codecs.lookup('bad')
Message
the subclass's: KeyError: 'id', IndexError: list index out of range
Quick fix
except LookupError: around a nested lookup
Watch out
list.index() and str.index() raise ValueError, not LookupError

Demo

Live evaluation
A key lookup followed by an index lookup — either step can fail, with a different subclass.
Try:
Inputs
keystrusers / admins / …
indexintlist position
Code
data = {'users': ['ann', 'bob'], 'admins': []}
data['users'][1]
Result
'bob'

One handler, two different exceptions: a wrong key gives KeyError with the key in quotes, a wrong position gives IndexError: list index out of range. type(e).__name__ tells you which one happened. Negative indexes count from the end, so users[-1] works but users[-3] does not.

Constructor

NameTypeRequiredDescription
*argsobjectnoUsually one message string, stored in e.args. Normally you raise KeyError or IndexError instead.

Attributes

AttributeTypeMeaning
argstupleFor KeyError, args[0] is the missing key; for IndexError, the message string.
__context__BaseException | NoneThe exception being handled when this one was raised.
__cause__BaseException | NoneSet by raise ... from ...

Common patterns

Safe nested lookup
Read a deep value from parsed JSON; any missing level falls back to a default.
def dig(data, *path, default=None):
    try:
        for step in path:
            data = data[step]
        return data
    except LookupError:
        return default

city = dig(payload, 'users', 0, 'address', 'city')
Raise it from a custom container
A __getitem__ that supports both keys and positions can raise the matching subclass — callers catching LookupError handle either.
class Registry:
    def __getitem__(self, item):
        if isinstance(item, int):
            if not 0 <= item < len(self._order):
                raise IndexError('registry index out of range')
            return self._items[self._order[item]]
        return self._items[item]  # KeyError if missing

Examples

1. KeyError and IndexError are LookupErrors
[issubclass(c, LookupError) for c in (KeyError, IndexError)]
Returns
[True, True]
2. One handler for both
def first_tag(post): try: return post['tags'][0] except LookupError: return None [first_tag({'tags': ['py']}), first_tag({'tags': []}), first_tag({})]
Returns
['py', None, None]
3. Missing index in nested data
{'items': []}['items'][0]
Returns
IndexError: list index out of range
4. Unknown codec raises LookupError itself
import codecs codecs.lookup('utf-99')
Returns
LookupError: unknown encoding: utf-99
5. .encode() with a bad codec name
'hi'.encode('latin-9x')
Returns
LookupError: unknown encoding: latin-9x
6. Tuples and strings raise IndexError too
'abc'[3]
Returns
IndexError: string index out of range

Pitfalls

1. .index() raises ValueError
Searching for a value is not a lookup by key or position: list.index and str.index raise ValueError, so except LookupError misses it.
except LookupError
try:
    pos = ['a', 'b'].index('z')
except LookupError:
    pos = -1
pos
ValueError: 'z' is not in list
except ValueError
try:
    pos = ['a', 'b'].index('z')
except ValueError:
    pos = -1
pos
-1
2. Attribute access is not a lookup
obj.name failing raises AttributeError, which is not a LookupError. Dicts need d[key], not d.key.
config.port
config = {'port': 80}
try:
    p = config.port
except LookupError:
    p = 8080
p
AttributeError: 'dict' object has no attribute 'port'
config['port']
config = {'port': 80}
try:
    p = config['port']
except LookupError:
    p = 8080
p
80
3. A wrong index type is TypeError
Indexing a list with a string (common with JSON: a list where you expected a dict) is a TypeError, not a LookupError.
except LookupError
data = [{'id': 1}]
try:
    r = data['id']
except LookupError:
    r = None
r
TypeError: list indices must be integers or slices, not str
index first
data = [{'id': 1}]
try:
    r = data[0]['id']
except LookupError:
    r = None
r
1

When to use

Use it
  • Walking nested dicts and lists from JSON, YAML or an API
  • A __getitem__ of your own that should be caught like a key or index miss
  • Handling codecs.lookup() / str.encode() with a user-supplied encoding name
Reach for something else
  • Only dicts involved → except KeyError is more precise
  • Only sequences involved → except IndexError
  • Need a default for one key → d.get(key, default)

Notes

CPython impl
Objects/exceptions.c — plain subclass of Exception; KeyError adds a repr-based __str__
Raised directly by
codecs.lookup() for an unknown encoding (and everything that uses it: str.encode, bytes.decode, open(encoding=…))
Not included
ValueError from .index(), AttributeError from obj.attr, TypeError from a wrong index type

FAQ

The base class for KeyError (missing mapping key) and IndexError (sequence index out of range). Catching LookupError handles both with one except clause. Python also raises LookupError directly from codecs.lookup() when an encoding name is unknown.