NousResearch/hermes-agent · error · ValueError

bad memory node id: {node_id!r}

Error message

bad memory node id: {node_id!r}

What it means

Raised by _parse_memory_id in the learning-mutation module when a memory node id does not match the required 'memory:<source>:<index>' shape, or the <source> segment is not one of the known memory files (MEMORY.md / USER.md). Every mutation API (edit, delete, inspect) parses the node id first, so any malformed id fails here.

Source

Thrown at agent/learning_mutations.py:40

_MEMORY_FILES = {"memory": "MEMORY.md", "profile": "USER.md"}


def parse_node_kind(node_id: str) -> str:
    return "memory" if node_id.startswith("memory:") else "skill"


def _memories_dir() -> Path:
    from hermes_constants import get_hermes_home

    return get_hermes_home() / "memories"


def _parse_memory_id(node_id: str) -> tuple[str, int]:
    """``memory:<source>:<index>`` → (source, global_index)."""
    parts = node_id.split(":", 2)
    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:

View on GitHub (pinned to c896c09c42)

Solutions

  1. Regenerate the learning graph (learning_graph module) and copy the exact node id it emits — ids must be 'memory:<source>:<global_index>'.
  2. Check the source segment is exactly 'memory' or 'user' (the keys of _MEMORY_FILES); other sources like 'skill' belong to a different node type.
  3. Ensure the third segment is a plain integer with no extra characters (no whitespace, no negative sign, no hex).

Example fix

# before
learning_mutations.node_detail("memory:MEMORY.md:3")  # ValueError: bad memory node id

# after
learning_mutations.node_detail("memory:memory:3")  # source must be a _MEMORY_FILES key
Defensive patterns

Strategy: validation

Validate before calling

import re
from agent.learning_mutations import _MEMORY_FILES

_VALID_ID = re.compile(r"^memory:(\w+):(\d+)$")

def is_valid_memory_id(node_id: str) -> bool:
    m = _VALID_ID.match(node_id or "")
    return bool(m) and m.group(1) in _MEMORY_FILES

Type guard

def is_valid_memory_id(node_id: str) -> bool:
    """Narrow a node id to the 'memory:<source>:<int>' shape with a known source."""
    m = _VALID_ID.match(node_id or "")
    return bool(m) and m.group(1) in _MEMORY_FILES

Try / catch

try:
    mutate(node_id)
except ValueError as e:
    if "bad memory node id" in str(e):
        node_id = regenerate_id_from_fresh_graph()
    else:
        raise

Prevention

When it happens

Trigger: Passing an id like 'memory:memory' (missing index), 'skill:python:3' (wrong prefix), 'memory:unknown:2' (source not in _MEMORY_FILES), or 'memory:memory:abc' (non-integer index — the same message is reused at the int() failure site).

Common situations: Agent/model hallucinating a node id instead of copying one from learning_graph output; stale ids from an older id scheme; string manipulation bugs that drop the index segment.

Related errors


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