{"record":{"id":"da7777f5bb229bc9","repo":"earendil-works/pi","slug":"invalid-lane","errorCode":"invalid_lane","errorMessage":"Lane not found: ${lane}","messagePattern":"Lane not found: (.+?)","errorType":"exception","errorClass":"SessionError","httpStatus":null,"severity":"error","filePath":"packages/agent/src/harness/session/session.ts","lineNumber":229,"sourceCode":"\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]> {\n\t\treturn this.queryRecords(query);\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\tassertValidLimit(options?.limit);\n\t\treturn this.storage.findOpenOperations(lane, options);\n\t}\n\n\tasync getLog(options?: LogOptions): Promise<LogItem[]> {\n\t\treturn this.queryLog(options);\n\t}\n\n\t/** Returns the lane's current leaf, or null when empty. Throws when the lane does not exist. */\n\tprivate async getLeafIdForLane(lane: string): Promise<string | null> {\n\t\tconst pointer = (await this.getLanes()).find((candidate) => candidate.lane === lane);\n\t\tif (!pointer) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\t\treturn pointer.leafId;\n\t}\n\n\tprivate async queryEntries(query: EntryQuery = {}, resultLimit = query.limit): Promise<Entry[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\treturn this.storage.findEntries(resultLimit === query.limit ? query : { ...query, limit: resultLimit });\n\t}\n\n\t/**\n\t * Queries from `query.start` toward the root, defaulting to the lane's current leaf.\n\t * `resultLimit` lets single-entry queries cap results without changing the caller's query.\n\t */\n\tprivate async queryBranchEntries(\n\t\tdefaultLane: string,\n\t\tquery: EntryQuery & BranchBounds = {},\n\t\tresultLimit = query.limit,\n\t): Promise<Entry[]> {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/earendil-works/pi/blob/4af9d21d3b4d664e4a29fcabfec85171077248e3/packages/agent/src/harness/session/session.ts#L211-L247","documentation":"A session tree is partitioned into lanes; only 'main' exists initially and every other lane must be created with createLane(). getLeafIdForLane resolves a lane name to its current leaf and throws invalid_lane when the lane is unknown. It backs session.view(lane), getLeafId() on views, and branch queries (findEntriesOnBranch / findEntryOnBranch) that omit start, because start defaults to the lane's leaf.","triggerScenarios":"session.view('draft').getLeafId() before createLane('draft', null) has run; case typos such as 'Main' (lane names are case-sensitive and the default is lowercase 'main'); referencing a lane that only exists in a different session; using any lane other than 'main' on a fork created with non-tree scope, which collapses the fork to the main lane.","commonSituations":"Lane names assembled from user or LLM output without validation; assuming fork copies lanes (only scope: 'tree' does); an earlier createLane call failing upstream; renaming lanes by convention without migrating stored names.","solutions":["Create the lane before use: await session.createLane('draft', null)","Check existence first: (await session.getLanes()).some((p) => p.lane === lane)","Use exactly 'main' (lowercase) for the default lane","After a non-tree fork, recreate side lanes before referencing them"],"exampleFix":"// before\nconst leaf = await session.view('draft').getLeafId();\n// after\nif (!(await session.getLanes()).some((p) => p.lane === 'draft')) {\n  await session.createLane('draft', null);\n}\nconst leaf = await session.view('draft').getLeafId();","handlingStrategy":"validation","validationCode":"const laneExists = async (session: Session, lane: string): Promise<boolean> =>\n  (await session.getLanes()).some((p) => p.lane === lane);\n\nif (!(await laneExists(session, 'draft'))) {\n  await session.createLane('draft', null);\n}","typeGuard":null,"tryCatchPattern":"try {\n  leaf = await session.view('draft').getLeafId();\n} catch (error) {\n  if (error instanceof SessionError && error.code === 'invalid_lane') {\n    await session.createLane('draft', null);\n    leaf = await session.view('draft').getLeafId(); // or fall back to the 'main' view\n  } else {\n    throw error;\n  }\n}","preventionTips":["Create lanes eagerly at session setup instead of lazily before reads","Validate lane names from user or LLM input against getLanes()","Use lowercase 'main' for the default lane","Recreate side lanes after a non-tree fork"],"tags":["lane","session","not-found","tree"],"backgroundTag":"unknown-lane-reference","analyzedSha":"4af9d21d3b4d664e4a29fcabfec85171077248e3","analyzedAt":"2026-08-24T13:07:14.692Z","schemaVersion":2},"datasetVersion":"2026-08-24T17:17:21.512Z"}