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

  1. 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.
  2. Use membership tests ("trail" in trails) and get() instead of iterating keys where possible.
  3. Persist keys before finalize() (e.g. write the build-mode keys to a file) and read them back when enumeration is needed.
  4. 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

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


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)