earendil-works/pi · error · SessionError
invalid_lane
invalid_lane
Error message
Lane not found: ${lane} What it means
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.
Source
Thrown at packages/agent/src/harness/session/session.ts:229
): Promise<Extract<LaneRecord, { type: K }>[]>;
async findRecords(query?: RecordQuery): Promise<LaneRecord[]>;
async findRecords(query?: RecordQuery): Promise<LaneRecord[]> {
return this.queryRecords(query);
}
async findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {
assertValidLimit(options?.limit);
return this.storage.findOpenOperations(lane, options);
}
async getLog(options?: LogOptions): Promise<LogItem[]> {
return this.queryLog(options);
}
/** Returns the lane's current leaf, or null when empty. Throws when the lane does not exist. */
private async getLeafIdForLane(lane: string): Promise<string | null> {
const pointer = (await this.getLanes()).find((candidate) => candidate.lane === lane);
if (!pointer) throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
return pointer.leafId;
}
private async queryEntries(query: EntryQuery = {}, resultLimit = query.limit): Promise<Entry[]> {
assertValidLimit(query.limit);
assertValidCursor(query.cursor?.afterSeq);
return this.storage.findEntries(resultLimit === query.limit ? query : { ...query, limit: resultLimit });
}
/**
* Queries from `query.start` toward the root, defaulting to the lane's current leaf.
* `resultLimit` lets single-entry queries cap results without changing the caller's query.
*/
private async queryBranchEntries(
defaultLane: string,
query: EntryQuery & BranchBounds = {},
resultLimit = query.limit,
): Promise<Entry[]> {View on GitHub (pinned to 4af9d21d3b)
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
Example fix
// before
const leaf = await session.view('draft').getLeafId();
// after
if (!(await session.getLanes()).some((p) => p.lane === 'draft')) {
await session.createLane('draft', null);
}
const leaf = await session.view('draft').getLeafId(); Defensive patterns
Strategy: validation
Validate before calling
const laneExists = async (session: Session, lane: string): Promise<boolean> =>
(await session.getLanes()).some((p) => p.lane === lane);
if (!(await laneExists(session, 'draft'))) {
await session.createLane('draft', null);
} Try / catch
try {
leaf = await session.view('draft').getLeafId();
} catch (error) {
if (error instanceof SessionError && error.code === 'invalid_lane') {
await session.createLane('draft', null);
leaf = await session.view('draft').getLeafId(); // or fall back to the 'main' view
} else {
throw error;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24).
Data as JSON: /api/errors/da7777f5bb229bc9.
Report an issue: GitHub.