arduino/Arduino · error · KeyError

dictionary is empty

Error message

dictionary is empty

What it means

OrderedDict.popitem() raises KeyError('dictionary is empty') when called on an OrderedDict that contains no items, because there is no (key, value) pair to remove and return. The vendored ordered_dict.py checks `if not self:` first and aborts rather than unlinking from an empty linked list. Unlike a plain dict, the message explicitly names the empty-container cause instead of a missing key.

Source

Thrown at arduino-core/src/processing/app/i18n/python/requests/packages/urllib3/packages/ordered_dict.py:98

    def clear(self):
        'od.clear() -> None.  Remove all items from od.'
        try:
            for node in self.__map.itervalues():
                del node[:]
            root = self.__root
            root[:] = [root, root, None]
            self.__map.clear()
        except AttributeError:
            pass
        dict.clear(self)

    def popitem(self, last=True):
        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
        Pairs are returned in LIFO order if last is true or FIFO order if false.

        '''
        if not self:
            raise KeyError('dictionary is empty')
        root = self.__root
        if last:
            link = root[0]
            link_prev = link[0]
            link_prev[1] = root
            root[0] = link_prev
        else:
            link = root[1]
            link_next = link[1]
            root[1] = link_next
            link_next[0] = root
        key = link[2]
        del self.__map[key]
        value = dict.pop(self, key)
        return key, value

    # -- the following methods do not depend on the internal structure --

View on GitHub (pinned to a0df6e0e83)

Solutions

  1. Guard with `if od: od.popitem()` before popping.
  2. Wrap the call in try/except KeyError to treat empty as a normal stop condition.
  3. Replace the drain loop with `for _ in range(len(od)): od.popitem()` or iterate over list(od.items()).

Example fix

// before
while True:
    k, v = cache.popitem()
    process(k, v)
// after
while cache:
    k, v = cache.popitem()
    process(k, v)
Defensive patterns

Strategy: validation

Validate before calling

if od:  # or: len(od) > 0
    k, v = od.popitem()
else:
    k, v = None, None

Type guard

def can_popitem(od):
    return isinstance(od, dict) and len(od) > 0

Try / catch

try:
    k, v = od.popitem()
except KeyError:
    k, v = None, None  # dict is empty

Prevention

When it happens

Trigger: Calling od.popitem() (with or without last=True/False) on an OrderedDict created empty or whose entries were all already removed; also hit when a draining loop calls popitem() one time more than the dict size.

Common situations: Looping `while True: od.popitem()` to drain a cache or LRU without checking emptiness; reusing an OrderedDict after a previous drain pass; concurrent code removing items between a size check and the popitem() call.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of arduino/Arduino@a0df6e0e83 (2026-09-06). Data as JSON: /api/errors/10b0d4689ae97e90. Report an issue: GitHub.