{"record":{"id":"9be08c57521da748","repo":"NousResearch/hermes-agent","slug":"bad-memory-node-id-node-id-r","errorCode":null,"errorMessage":"bad memory node id: {node_id!r}","messagePattern":"bad memory node id: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/learning_mutations.py","lineNumber":40,"sourceCode":"\n_MEMORY_FILES = {\"memory\": \"MEMORY.md\", \"profile\": \"USER.md\"}\n\n\ndef parse_node_kind(node_id: str) -> str:\n    return \"memory\" if node_id.startswith(\"memory:\") else \"skill\"\n\n\ndef _memories_dir() -> Path:\n    from hermes_constants import get_hermes_home\n\n    return get_hermes_home() / \"memories\"\n\n\ndef _parse_memory_id(node_id: str) -> tuple[str, int]:\n    \"\"\"``memory:<source>:<index>`` → (source, global_index).\"\"\"\n    parts = node_id.split(\":\", 2)\n    if len(parts) != 3 or parts[0] != \"memory\" or parts[1] not in _MEMORY_FILES:\n        raise ValueError(f\"bad memory node id: {node_id!r}\")\n    try:\n        return parts[1], int(parts[2])\n    except ValueError as exc:\n        raise ValueError(f\"bad memory node id: {node_id!r}\") from exc\n\n\ndef _memory_local_index(source: str, global_index: int) -> int:\n    \"\"\"Global card index → position within the source's own file.\n\n    ``_memory_cards`` emits all ``MEMORY.md`` cards before ``USER.md`` cards, so\n    a profile card's local index is its global index minus the memory count.\n    \"\"\"\n    from agent.learning_graph import _memory_cards\n\n    cards = _memory_cards()\n    if not 0 <= global_index < len(cards):\n        raise IndexError(f\"memory index {global_index} out of range\")\n    if cards[global_index].get(\"source\") != source:","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/learning_mutations.py#L22-L58","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Regenerate the learning graph (learning_graph module) and copy the exact node id it emits — ids must be 'memory:<source>:<global_index>'.","Check the source segment is exactly 'memory' or 'user' (the keys of _MEMORY_FILES); other sources like 'skill' belong to a different node type.","Ensure the third segment is a plain integer with no extra characters (no whitespace, no negative sign, no hex)."],"exampleFix":"# before\nlearning_mutations.node_detail(\"memory:MEMORY.md:3\")  # ValueError: bad memory node id\n\n# after\nlearning_mutations.node_detail(\"memory:memory:3\")  # source must be a _MEMORY_FILES key","handlingStrategy":"validation","validationCode":"import re\nfrom agent.learning_mutations import _MEMORY_FILES\n\n_VALID_ID = re.compile(r\"^memory:(\\w+):(\\d+)$\")\n\ndef is_valid_memory_id(node_id: str) -> bool:\n    m = _VALID_ID.match(node_id or \"\")\n    return bool(m) and m.group(1) in _MEMORY_FILES","typeGuard":"def is_valid_memory_id(node_id: str) -> bool:\n    \"\"\"Narrow a node id to the 'memory:<source>:<int>' shape with a known source.\"\"\"\n    m = _VALID_ID.match(node_id or \"\")\n    return bool(m) and m.group(1) in _MEMORY_FILES","tryCatchPattern":"try:\n    mutate(node_id)\nexcept ValueError as e:\n    if \"bad memory node id\" in str(e):\n        node_id = regenerate_id_from_fresh_graph()\n    else:\n        raise","preventionTips":["Always copy node ids verbatim from a freshly rendered learning graph.","Validate the id shape (memory:<known-source>:<int>) before calling any mutation API.","Construct ids only via f\"memory:{source}:{int(idx)}\" so the index is canonicalized."],"tags":["memory","learning-graph","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}