{"record":{"id":"63be8cd63ea067a3","repo":"stamparm/maltrail","slug":"cannot-modify-a-finalized-trailsdict","errorCode":null,"errorMessage":"cannot modify a finalized TrailsDict","messagePattern":"cannot modify a finalized TrailsDict","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/trailsdict.py","lineNumber":270,"sourceCode":"        while i < n and hi[i] == target_hi:\n            if lo[i] == target_lo:\n                return pair_list[values[i]]\n            i += 1\n        return default\n\n    def __len__(self):\n        mm = self._mmap\n        if mm is not None:\n            return mm[\"length\"]\n        frozen = self._frozen\n        return frozen[5] if frozen is not None else len(self._trails)\n\n    def clear(self):\n        self.__init__()\n\n    def __setitem__(self, key, value):\n        if self._frozen is not None or self._mmap is not None:\n            raise Exception(\"cannot modify a finalized TrailsDict\")\n        if not isinstance(value, (tuple, list)):\n            raise Exception(\"unsupported type '%s'\" % type(value))\n\n        pair = (value[0], value[1])\n        shared = self._pairs.get(pair)\n        if shared is None:\n            shared = pair\n            self._pairs[pair] = pair\n        self._trails[key] = shared\n\n    def __delitem__(self, key):\n        if self._frozen is not None or self._mmap is not None:\n            raise Exception(\"cannot modify a finalized TrailsDict\")\n        del self._trails[key]\n\n    def update(self, value):\n        if self._frozen is not None or self._mmap is not None:\n            raise Exception(\"cannot modify a finalized TrailsDict\")","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/core/trailsdict.py#L252-L288","documentation":"This is a generic lifecycle-state guard in TrailsDict.__setitem__: once the dict has been finalized — either frozen or switched to a memory-mapped read-only backend (self._frozen / self._mmap set) — the internal index structures are considered immutable, so any key assignment is refused. It fires when application code keeps a reference to a TrailsDict after finalization (e.g. after freeze or loading from a bin file) and then attempts to store or overwrite a trail. Fix by performing all mutations before finalizing, or by creating a new mutable TrailsDict instance (e.g. via clear()/constructor) instead of writing to the finalized one.","triggerScenarios":"trails.freeze() followed by trails['x'] = (1,2); assigning into a TrailsDict loaded from a memory-mapped bin; an update()/build path that finishes and freezes before a late writer writes.","commonSituations":"Code holding a reference to a frozen dict shared across threads/sensors; re-running an updater against an already-finalized dict; forgetting to re-create (or clear) the dict before a new update cycle.","solutions":["Create a new TrailsDict for updates instead of writing to the finalized one","Delay freeze()/bin-loading until all writes are complete","Call clear() (which re-inits) before writing if a fresh empty dict is intended","Guard writes with a check for the frozen/mmap state and skip or log instead"],"exampleFix":"// before\nif trail_changed:\n    trails[key] = value  # trails already frozen\n// after\nif trail_changed and trails._frozen is None and trails._mmap is None:\n    trails[key] = value","handlingStrategy":"type-guard","validationCode":"def trails_writable(td):\n    return td._frozen is None and td._mmap is None","typeGuard":"def is_writable_trailsdict(td):\n    return isinstance(td, TrailsDict) and td._frozen is None and td._mmap is None","tryCatchPattern":"try:\n    trails[key] = value\nexcept Exception as e:\n    if \"finalized\" in str(e):\n        trails = TrailsDict(); trails[key] = value  # start a fresh cycle","preventionTips":["Treat frozen/mmap-backed dicts as strictly read-only","Build updates in a new dict and swap atomically","Audit code paths that retain references past finalization","Coordinate threads with a flag/lock around finalization"],"tags":["python","immutable-state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}