{"record":{"id":"cf5c751bab22db08","repo":"kovidgoyal/kitty","slug":"cannot-access-the-mmap-of-a-closed-shared-memory-o","errorCode":null,"errorMessage":"Cannot access the mmap of a closed shared memory object","messagePattern":"Cannot access the mmap of a closed shared memory object","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"kitty/shm.py","lineNumber":157,"sourceCode":"\n    def __exit__(self, *a: object) -> None:\n        self.close()\n        if self.unlink_on_exit:\n            self.unlink()\n\n    @property\n    def size(self) -> int:\n        return self._size\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @property\n    def mmap(self) -> mmap.mmap:\n        ans = self._mmap\n        if ans is None:\n            raise RuntimeError('Cannot access the mmap of a closed shared memory object')\n        return ans\n\n    def fileno(self) -> int:\n        return self._fd\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}({self.name!r}, size={self.size})'\n\n    def close(self) -> None:\n        \"\"\"Closes access to the shared memory from this instance but does\n        not destroy the shared memory block.\"\"\"\n        if self._mmap is not None:\n            try:\n                self._mmap.close()\n            except BufferError:\n                if not self.ignore_close_failure:\n                    raise\n            self._mmap = None","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kitty/shm.py#L139-L175","documentation":"The `mmap` property was accessed after the SharedMemory object was closed. Close() unmaps the memory and sets the internal _mmap to None, so any later access to .mmap raises RuntimeError instead of returning a dangling pointer. This is a use-after-close guard.","triggerScenarios":"Accessing shm.mmap after calling shm.close(); also in a __del__/destructor path or another thread that races with close().","commonSituations":"Forgetting close() was called in a cleanup/finally block; two processes/threads sharing the object where one closes it; storing a reference to the object beyond its intended lifetime.","solutions":["Keep the SharedMemory object alive as long as any mmap/NumPy view derived from it is used; close only at the very end","Audit cleanup paths (finally blocks, context managers) for premature close()","Cache the mmap object before closing is possible only if you also keep the fd/mapping alive — instead, restructure so close happens last","If another thread closes it, coordinate with a lock or lifetime owner"],"exampleFix":"// before\nbuf = shm.mmap\nshm.close()\nuse(buf)  # buf is fine, but any later shm.mmap access is not\nx = shm.mmap  # RuntimeError\n// after\nbuf = shm.mmap\nuse(buf)\nshm.close()  # close last, after all uses","handlingStrategy":"type-guard","validationCode":"if shm._mmap is None:\n    raise RuntimeError('already closed')  # or skip work\n# safer: track closed state yourself\nclosed = False\n# ... set closed = True in your own close wrapper","typeGuard":"def is_open(shm) -> bool:\n    import types\n    return getattr(shm, '_mmap', None) is not None","tryCatchPattern":"try:\n    mm = shm.mmap\nexcept RuntimeError:\n    shm = SharedMemory(name=name)  # reopen if needed","preventionTips":["Own the lifetime: one owner closes, after all consumers are done","Grab shm.mmap once at startup and reuse the reference; don't re-access after close","In multithreaded code, guard close() with the same lock as mmap access"],"tags":["shared-memory","use-after-close","lifetime"],"backgroundTag":"use-after-close","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}