shareAI-lab/learn-claude-code · warning · ValueError

The memory index is not a memory record

Error message

The memory index is not a memory record

What it means

Raised by memory_path() in s09_memory/code.py when the caller passes the memory index file's name (MEMORY_INDEX.name, e.g. INDEX.md) without allow_index=True. The index is a generated catalog of memory records, not a record itself, so record-oriented tools (read/write/delete a memory) refuse to operate on it to prevent corruption or recursive self-reference. Only the dedicated index-rebuild path passes allow_index=True.

Source

Thrown at s09_memory/code.py:92

    if len(parts) < 3:
        return {}, text
    try:
        metadata = yaml.safe_load(parts[1]) or {}
    except yaml.YAMLError:
        return {}, text
    if not isinstance(metadata, dict):
        return {}, text
    return metadata, parts[2].lstrip()

def memory_slug(name: str) -> str:
    slug = re.sub(r"[^\w]+", "-", name.lower()).strip("-_")
    return slug or "memory"

def memory_path(filename: str, allow_index: bool = False) -> Path:
    if Path(filename).name != filename:
        raise ValueError(f"Invalid memory filename: {filename}")
    if filename == MEMORY_INDEX.name and not allow_index:
        raise ValueError("The memory index is not a memory record")

    root = MEMORY_DIR.resolve()
    if not root.is_relative_to(WORKDIR.resolve()):
        raise ValueError("Memory directory escapes the workspace")
    path = (root / filename).resolve()
    if not path.is_relative_to(root):
        raise ValueError(f"Memory path escapes the store: {filename}")
    return path

def _memory_slug(name: str) -> str:
    return memory_slug(name)

def _normalized_memory_text(value: str) -> str:
    return " ".join(value.lower().split())

def should_store_memory(candidate: dict, existing: list[dict]) -> bool:
    """Accept durable records that are not temporary or already stored."""
    if not isinstance(candidate, dict):

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Filter the index filename out of any directory listing before per-record calls
  2. Choose memory names that do not slug down to the index filename
  3. Only the index-maintenance code path should pass allow_index=True; never expose that flag through the agent tool surface

Example fix

// before
for f in MEMORY_DIR.iterdir():
    memory_read(f.name)  # blows up on INDEX file
// after
for f in MEMORY_DIR.iterdir():
    if f.name != MEMORY_INDEX.name:
        memory_read(f.name)
Defensive patterns

Strategy: validation

Validate before calling

INDEX_NAME = MEMORY_INDEX.name  # e.g. 'INDEX.md'
records = [f for f in MEMORY_DIR.iterdir() if f.name != INDEX_NAME]
for f in records:
    memory_read(f.name)

Type guard

def is_memory_record(filename: str) -> bool:
    return filename != MEMORY_INDEX.name and Path(filename).name == filename

Try / catch

try:
    memory_path(fn)
except ValueError as e:
    if 'index is not a memory record' in str(e):
        skip = True  # it's the catalog, not a record
    else:
        raise

Prevention

When it happens

Trigger: A memory tool call with filename equal to the index file's name; listing or globbing MEMORY_DIR and feeding every resulting filename back into a per-record tool without filtering out the index; a memory slug that happens to collide with the index filename.

Common situations: Agent iterating directory contents and treating every file as a record; user-visible index file tempting direct edits; slug collisions when memory names sanitize to 'index'.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/e065285251c49251. Report an issue: GitHub.