stamparm/maltrail · error
cannot iterate a finalized TrailsDict (keys are not…
Error message
cannot iterate a finalized TrailsDict (keys are not retained)
What it means
After finalize(), key strings are deliberately discarded to save ~90MB per 1.6M trails; only 64-bit hashes packed into an array remain. keys() therefore cannot return the original keys and raises this exception on a finalized (or mmap-backed) instance rather than returning a wrong/partial result. Lookups, membership tests, get(), and len() still work.
Solutions
- Keep the pre-finalization build-mode dict (or a serialized key list) if you need key enumeration; finalize a separate copy used only for lookups.
- Use membership tests ("trail" in trails) and get() instead of iterating keys where possible.
- Persist keys before finalize() (e.g. write the build-mode keys to a file) and read them back when enumeration is needed.
- Refactor to pass the build-mode TrailsDict to the consumer; only the sensor hot path needs the finalized one.
Example fix
// before
trails.finalize()
for key in trails.keys(): # Exception
log(key)
// after
keys_snapshot = list(trails.keys()) # before finalize
trails.finalize()
for key in keys_snapshot:
log(key) Defensive patterns
Strategy: validation
Validate before calling
if trails._frozen is not None or trails._mmap is not None:
raise RuntimeError("keys are not retained after finalize(); snapshot keys before finalizing") Type guard
def can_iterate_keys(t):
return isinstance(t, TrailsDict) and t._frozen is None and t._mmap is None Try / catch
try:
keys = list(trails.keys())
except Exception:
keys = load_persisted_key_snapshot() # saved before finalize() Prevention
- Snapshot list(trails.keys()) before calling finalize().
- On finalized dicts use only membership tests, get(), and len().
- Persist the build-mode key list to disk if post-finalize enumeration is foreseeable.
- Keep the enum-needed consumer pointed at a build-mode copy.
When it happens
Trigger: Calling trails.keys(), list(trails), dict(trails), or any iteration over keys after finalize() or open_mmap(). Note keys() itself checks eagerly, so even an unchecked .keys() (no list()) throws.
Common situations: Debug/logging code that dumps trail names after the sensor finalized; code computing set differences (set(trails) - known) post-finalize; pickling/copying via dict(trails); doctests or unit tests that enumerate keys on a finalized store.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- [x] invalid IP address
- not a Maltrail provenance sidecar (bad magic)
- provenance sidecar is truncated
- packet too short for header-protection sample
- trail bin too small
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/c701bfa3ca79d61d.
Report an issue: GitHub.
Appendix: source
Thrown at core/trailsdict.py:297
self._trails[key] = shared
def __delitem__(self, key):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot modify a finalized TrailsDict")
del self._trails[key]
def update(self, value):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot modify a finalized TrailsDict")
if isinstance(value, (TrailsDict, dict)):
for key in value:
self[key] = value[key]
else:
raise Exception("unsupported type '%s'" % type(value))
def keys(self):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot iterate a finalized TrailsDict (keys are not retained)")
return self._trails.keys()
def iterkeys(self):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot iterate a finalized TrailsDict (keys are not retained)")
for key in self._trails:
yield key
# NOTE: items()/values() are NOT inherited from dict here - the dict base is always empty (all data lives in
# self._trails), so the inherited versions would silently return nothing. Route them to _trails, matching keys().
def items(self):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot iterate a finalized TrailsDict (keys are not retained)")
return self._trails.items()
def values(self):
if self._frozen is not None or self._mmap is not None:
raise Exception("cannot iterate a finalized TrailsDict (keys are not retained)")View on GitHub (pinned to 77cfb06d76)