earendil-works/pi · error · SessionError
not_found
not_found
Error message
Entry not found: ${targetId} What it means
SessionState.validateTarget throws not_found when a non-null targetId is not a known entry id (entriesById lookup, state.ts:88). It guards createLane, moveLane and setLabel, so every lane target and label target must be an existing entry in the same SessionState. Record ids (tool runs, usage) and lane names are not entry ids and are rejected.
Source
Thrown at packages/agent/src/harness/session/state.ts:89
}
getLanes(): LanePointer[] {
return [...this.lanes].map(([lane, leafId]) => ({ lane, leafId }));
}
requireLane(lane: string): string | null {
const leafId = this.lanes.get(lane);
if (leafId === undefined) throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
return leafId;
}
validateNewLane(lane: string): void {
if (this.lanes.has(lane)) throw new SessionError("already_exists", `Lane already exists: ${lane}`);
}
validateTarget(targetId: string | null): void {
if (targetId !== null && !this.entriesById.has(targetId)) {
throw new SessionError("not_found", `Entry not found: ${targetId}`);
}
}
validateUnusedId(id: string): void {
if (this.usedIds.has(id)) throw new SessionError("already_exists", `Session id already exists: ${id}`);
}
applyMutation(mutation: SessionMutation, invalid: InvalidMutation = invalidMutation): void {
const seq =
mutation.kind === "entry"
? mutation.entry.seq
: mutation.kind === "record"
? mutation.record.seq
: mutation.seq;
if (seq !== this.sequence + 1) invalid(`has non-consecutive seq ${seq}`);
switch (mutation.kind) {
case "entry": {View on GitHub (pinned to 4af9d21d3b)
Solutions
- Resolve target ids from state.findEntries() or getEntry() on the same instance
- Use lane leaf ids from getLanes()/requireLane() as move targets
- When sourcing ids from getLog(), use only items with kind === 'entry' (item.entry.id)
- Catch 'not_found' and re-fetch current leaf ids before retrying once
Example fix
// before
const run = state.findOpenOperations('main')[0]!;
session.moveLane('draft', run.id); // record id -> not_found
// after
const last = state.findEntries({ type: 'message' }).at(-1)!;
session.moveLane('draft', last.id); Defensive patterns
Strategy: validation
Validate before calling
// Verify the target is an existing entry before using it
if (targetId !== null && state.getEntry(targetId) === undefined) {
throw new Error('stale target id: ' + targetId);
}
session.moveLane(lane, targetId); Type guard
function isEntryId(state: SessionState, id: string): boolean {
return state.getEntry(id) !== undefined;
} Try / catch
try {
session.setLabel(targetId, label);
} catch (e) {
if (isSessionError(e, 'not_found')) {
const leaf = state.requireLane(lane); // refresh and retry once with a known-good id
if (leaf !== null) session.setLabel(leaf, label);
} else throw e;
} Prevention
- Only use ids returned by findEntries(), getEntry() or getLanes() on the same SessionState instance
- Never feed record ids (findRecords, findOpenOperations, runIds) as entry targets
- Refresh cached ids after rehydrating or rebuilding a session
When it happens
Trigger: moveLane(lane, entryId) where entryId came from findRecords()/findOpenOperations() (a LaneRecord id, not an Entry id); setLabel on an entry id captured from a different session instance; createLane pointing at a leafId taken before the session was rebuilt or forked.
Common situations: Confusing record ids with entry ids because getLog() interleaves both kinds; copying ids from a persisted session file into a fresh SessionState; using stale ids after an external prune or compaction removed entries.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24).
Data as JSON: /api/errors/7e93ca21a7e0e00b.
Report an issue: GitHub.