Skip to content

bug: malformed hallways.json records crash hallway operations #2270

Description

@fallenmi

What happened?

_load_hallways() accepts both the current {"schema_version": 1, "hallways": [...]} envelope and the legacy bare-list format, but it does not validate the envelope payload or individual records before returning them. Core consumers then assume every element is a hallway dict with a usable ID, wing, and entity pair.

One malformed value can therefore block every valid hallway in the same file:

  • null, strings, numbers, lists, and partial dicts raise during .get() access or entity-pair sorting;
  • a truthy non-list hallways payload leaks dict keys or string characters through unfiltered listing and crashes filtered operations;
  • a nonnumeric co_occurrence_count can crash mempalace hallways while sorting mixed types;
  • malformed rooms values are unsafe for consumers that iterate room names;
  • invalid UTF-8, JSON decoder limit errors, and escaped lone surrogates escape the intended corrupt-file/save-safe behavior.

Affected MCP list/delete calls surface as generic internal tool errors.

What did you expect?

The loader should preserve usable hallway records while skipping malformed content:

  • require non-empty string id, wing, entity_a, and entity_b fields;
  • when present, require a numeric co_occurrence_count and a list of string rooms;
  • preserve unrelated optional fields when the record is safe to persist as UTF-8;
  • support both the envelope and legacy bare-list formats;
  • emit one aggregated warning containing only the path and skipped count, never record contents;
  • diagnose unsupported root/payload types without auto-wrapping them;
  • never rewrite the sidecar during a read.

A later successful mutation may naturally persist only the valid records through the existing atomic write path; an unsuccessful delete should preserve the original bytes.

How to reproduce:

  1. On current develop, point the hallway resolver at a temporary file.
  2. Write:
{"schema_version": 1, "hallways": [null, {"id": "h1", "wing": "wing_a", "entity_a": "A", "entity_b": "B"}]}
  1. Call list_hallways("wing_a"), delete_hallway("h1"), or compute_hallways_for_wing() with a qualifying entity pair.
  2. Each path raises AttributeError: 'NoneType' object has no attribute 'get'.
  3. Separately, store one valid-shaped record with "co_occurrence_count": "not-a-number" beside a numeric record and run mempalace hallways; sorting raises TypeError.

Environment:

  • OS: macOS arm64
  • Python version: 3.12.12
  • MemPal version: develop at 06cb6987f02610784fefbad4b2bd5d026d164ba6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions