{"record":{"id":"c44a7eff2bceb812","repo":"can1357/oh-my-pi","slug":"entry-parentid-not-found","errorCode":null,"errorMessage":"Entry ${parentId} not found","messagePattern":"Entry (.+?) not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/session-manager.ts","lineNumber":2287,"sourceCode":"\t\tthis.#recordEntry(entry);\n\t\treturn entry.id;\n\t}\n\n\t/**\n\t * Append to a non-active branch without changing the current leaf.\n\t * Used by work that retains ownership of a branch across tree navigation.\n\t */\n\tappendMessageToBranch(\n\t\tmessage:\n\t\t\t| Message\n\t\t\t| CustomMessage\n\t\t\t| HookMessage\n\t\t\t| BashExecutionMessage\n\t\t\t| PythonExecutionMessage\n\t\t\t| FileMentionMessage,\n\t\tparentId: string | null,\n\t): string {\n\t\tif (parentId !== null && !this.#index.has(parentId)) throw new Error(`Entry ${parentId} not found`);\n\t\tconst activeLeafId = this.#index.leafId();\n\t\tconst entry: SessionMessageEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid: generateId(this.#index),\n\t\t\tparentId,\n\t\t\ttimestamp: nowIso(),\n\t\t\tmessage,\n\t\t};\n\t\tthis.#recordEntry(entry);\n\t\tthis.#index.setLeaf(activeLeafId);\n\t\treturn entry.id;\n\t}\n\n\t/** Append a thinking level change as child of current leaf, then advance leaf. Returns entry id. */\n\tappendThinkingLevelChange(thinkingLevel?: string, configured?: string): string {\n\t\tconst entry: ThinkingLevelChangeEntry = {\n\t\t\ttype: \"thinking_level_change\",\n\t\t\t...this.#freshEntryFields(),","sourceCodeStart":2269,"sourceCodeEnd":2305,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/session-manager.ts#L2269-L2305","documentation":"appendMessage records a message entry with an explicit parentId to build the session's entry tree. If a non-null parentId is not present in the session entry index, the tree would be corrupted (an orphan entry), so the call throws. parentId must reference an entry already recorded in this session, or be null to append at the root.","triggerScenarios":"Calling appendMessage with a parentId that was never returned by this session manager (entry from another session, entry pruned by compaction, an id from a file loaded into a different manager instance, or a stale id after branching/undo).","commonSituations":"Reusing entry IDs across session instances; referencing an entry removed/compacted away; passing a fabricated id; resuming a different session file while holding old entry ids in memory.","solutions":["Use ids only from the same SessionManager instance (values returned by append*/getEntry APIs).","Pass null as parentId to append at the current leaf instead of a stale id.","Re-resolve the parent id via the entry index/leaf API after compaction or branching.","Check that the right session (file) is loaded before appending."],"exampleFix":"// before\nmgr.appendMessage(msg, staleEntryId); // Entry <id> not found\n// after\nconst parentId = mgr.getLeafId(); // current valid entry id or null\nmgr.appendMessage(msg, parentId);","handlingStrategy":"validation","validationCode":"if (parentId !== null && !mgr.hasEntry(parentId)) {\n  parentId = null; // append at leaf instead of orphaning\n}","typeGuard":"function entryExists(mgr, id) {\n  return id === null || mgr.getEntry(id) != null;\n}","tryCatchPattern":"try {\n  mgr.appendMessage(msg, parentId);\n} catch (err) {\n  if (/Entry .* not found/.test(err.message)) {\n    mgr.appendMessage(msg, mgr.getLeafId());\n  } else throw err;\n}","preventionTips":["Only use entry ids returned by the same SessionManager instance.","Re-resolve ids after compaction, branching, or session reload.","Default to null parentId (append at leaf) unless you track the parent explicitly."],"tags":["session","entry-tree","invalid-argument"],"backgroundTag":"entry-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}