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

  1. Create the lane before use: await session.createLane('draft', null)
  2. Check existence first: (await session.getLanes()).some((p) => p.lane === lane)
  3. Use exactly 'main' (lowercase) for the default lane
  4. 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

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.