{"record":{"id":"822980b9d3b2a1b9","repo":"python/cpython","slug":"pop-from-empty-weakset","errorCode":null,"errorMessage":"pop from empty WeakSet","messagePattern":"pop from empty WeakSet","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"Lib/_weakrefset.py","lineNumber":59,"sourceCode":"\n    def __reduce__(self):\n        return self.__class__, (list(self),), self.__getstate__()\n\n    def add(self, item):\n        self.data.add(ref(item, self._remove))\n\n    def clear(self):\n        self.data.clear()\n\n    def copy(self):\n        return self.__class__(self)\n\n    def pop(self):\n        while True:\n            try:\n                itemref = self.data.pop()\n            except KeyError:\n                raise KeyError('pop from empty WeakSet') from None\n            item = itemref()\n            if item is not None:\n                return item\n\n    def remove(self, item):\n        self.data.remove(ref(item))\n\n    def discard(self, item):\n        self.data.discard(ref(item))\n\n    def update(self, other):\n        for element in other:\n            self.add(element)\n\n    def __ior__(self, other):\n        self.update(other)\n        return self\n","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_weakrefset.py#L41-L77","documentation":"WeakSet.pop loops popping references from the internal set self.data; if that set is empty the underlying set.pop raises KeyError, which is re-raised (chained suppression via `from None`) as KeyError('pop from empty WeakSet'). The loop also skips dead references, so a set holding only dead weakrefs also behaves as empty.","triggerScenarios":"WeakSet().pop(); or .pop() on a WeakSet whose elements were garbage-collected (all remaining refs are dead) — mirroring set.pop semantics for emptiness.","commonSituations":"Using WeakSet for caches/observer registries and popping without checking; entries silently removed by GC make 'len()>0 just checked' races common in observer patterns.","solutions":["Guard with a length/emptiness check, tolerating the GC race: try: item = ws.pop() except KeyError: handle-empty","Prefer discard/remove or iteration when order does not matter","Keep strong references to items that must survive (e.g. a companion list) so GC cannot empty the set unexpectedly"],"exampleFix":"# before\nitem = ws.pop()  # KeyError: pop from empty WeakSet\n\n# after\ntry:\n    item = ws.pop()\nexcept KeyError:\n    item = None","handlingStrategy":"try-catch","validationCode":"def weakset_pop(ws):\n    try:\n        return ws.pop()\n    except KeyError:\n        return None  # or a sentinel / default","typeGuard":"def weakset_alive(ws) -> list:\n    return [r for r in ws if r is not None]","tryCatchPattern":"try:\n    item = observers.pop()\nexcept KeyError:  # includes 'pop from empty WeakSet'\n    item = None","preventionTips":["Treat WeakSet.pop like set.pop: expect KeyError when empty","Check emptiness with `if not ws:` right before popping, but still catch KeyError for the GC race","Hold strong references to entries that must not vanish (companion list)"],"tags":["weakref","weakset","keyerror","garbage-collection","cpython"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}