NousResearch/hermes-agent · error · IndexError

memory index {global_index} out of range

Error message

memory index {global_index} out of range

What it means

IndexError from _memory_local_index: the global index embedded in the node id is outside the current card list produced by learning_graph._memory_cards(). The graph is a snapshot; if memory files changed (cards added/removed) or the id was fabricated, the index no longer resolves.

Source

Thrown at agent/learning_mutations.py:57

    if len(parts) != 3 or parts[0] != "memory" or parts[1] not in _MEMORY_FILES:
        raise ValueError(f"bad memory node id: {node_id!r}")
    try:
        return parts[1], int(parts[2])
    except ValueError as exc:
        raise ValueError(f"bad memory node id: {node_id!r}") from exc


def _memory_local_index(source: str, global_index: int) -> int:
    """Global card index → position within the source's own file.

    ``_memory_cards`` emits all ``MEMORY.md`` cards before ``USER.md`` cards, so
    a profile card's local index is its global index minus the memory count.
    """
    from agent.learning_graph import _memory_cards

    cards = _memory_cards()
    if not 0 <= global_index < len(cards):
        raise IndexError(f"memory index {global_index} out of range")
    if cards[global_index].get("source") != source:
        raise ValueError("memory node id is stale — refresh the graph")
    if source == "memory":
        return global_index
    return global_index - sum(1 for c in cards if c.get("source") == "memory")


def _locate_memory(source: str, gidx: int) -> tuple[Path, list[str], int]:
    """Resolve a memory card to its file, all §-delimited entries, and local index.

    Entries come from ``MemoryStore._read_file`` — the same parser the memory
    tool uses — so journey indices stay aligned with what the graph renders.
    """
    from tools.memory_tool import MemoryStore

    path = _memories_dir() / _MEMORY_FILES[source]
    if not path.exists():
        raise ValueError(f"{path.name} not found")

View on GitHub (pinned to c896c09c42)

Solutions

  1. Re-render the learning graph and use a fresh node id from that output.
  2. Verify the correct profile is active (HERMES_HOME) — card counts differ per profile.
  3. If another process edits memory files, serialize mutations or re-check the graph immediately before mutating.
Defensive patterns

Strategy: validation

Validate before calling

from agent.learning_graph import _memory_cards

def index_in_range(global_index: int) -> bool:
    return 0 <= global_index < len(_memory_cards())

Try / catch

try:
    mutate(node_id)
except IndexError as e:
    if "out of range" in str(e):
        node_id = refresh_graph_and_rederive_id()
        mutate(node_id)
    else:
        raise

Prevention

When it happens

Trigger: Calling a mutation with 'memory:memory:<N>' where N >= len(_memory_cards()) or N < 0; typical after cards were deleted (shrinking the list) or when an id from a different profile/session is reused.

Common situations: Stale graph rendered before recent memory edits; concurrent mutation from another session that removed cards; profile mismatch (different HERMES_HOME has fewer memories).

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/75e78f133569b053. Report an issue: GitHub.