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
- Re-render the learning graph and use a fresh node id from that output.
- Verify the correct profile is active (HERMES_HOME) — card counts differ per profile.
- 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
- Check the index against the current card count immediately before mutating.
- Re-render the graph after any memory add/delete and before the next mutation.
- Treat node ids as ephemeral within a single render/mutate cycle.
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
- memory node id is stale — refresh the graph
- bad memory node id: {node_id!r}
- Session transcript exceeds the Desktop safe-load limit; use
- {path.name} not found
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/75e78f133569b053.
Report an issue: GitHub.