can1357/oh-my-pi · error · Error

Unable to read Claude session ${info.id}: ${detail}

Error message

Unable to read Claude session ${info.id}: ${detail}

What it means

ClaudeSessionStore.load() reads a Claude Code session file (JSONL) from disk and converts it into an OMP session. This error wraps any failure from the underlying record-collection or stat calls — file missing, permission denied, corrupted/unparseable JSON lines — embedding the original message as `detail`. It exists so callers get a uniform error naming the session id regardless of which low-level read failed.

Source

Thrown at packages/coding-agent/src/session/claude-session-store.ts:356

			} catch {
				// Files may disappear while Claude rotates its session store.
			}
		}
		return sessions.sort(
			(left, right) => right.modified.getTime() - left.modified.getTime() || left.path.localeCompare(right.path),
		);
	}

	/** Loads and converts a Claude transcript while preserving its source tree and timestamps. */
	async load(info: ForeignSessionInfo): Promise<SessionManager> {
		if (info.source !== this.source) throw new Error(`Cannot load ${info.source} session with ClaudeSessionStore`);
		let records: ForeignJsonRecord[];
		let stats: fsTypes.Stats;
		try {
			[records, stats] = await Promise.all([collectForeignJsonRecords(info.path), fs.stat(info.path)]);
		} catch (error) {
			const detail = error instanceof Error ? error.message : String(error);
			throw new Error(`Unable to read Claude session ${info.id}: ${detail}`);
		}
		if (records.length === 0 && stats.size > 0)
			throw new Error(`Claude session ${info.id} contains no readable records`);

		const sourceParents = new Map<string, string | null>();
		let sourceCwd: string | undefined;
		let sourceTitle: string | undefined;
		let aiTitle: string | undefined;
		for (const { value } of records) {
			const uuid = stringField(value, "uuid");
			if (uuid) sourceParents.set(uuid, stringField(value, "parentUuid") ?? null);
			if (!sourceCwd) sourceCwd = stringField(value, "cwd");
			if (value.type === "custom-title") sourceTitle = stringField(value, "customTitle") ?? sourceTitle;
			if (value.type === "ai-title") aiTitle = stringField(value, "aiTitle") ?? aiTitle;
		}

		const manager = SessionManager.inMemory(sourceCwd ?? info.cwd);
		const sourceTails = new Map<string, string>();

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file at info.path exists and is readable (ls -l / cat the JSONL path shown in the error)
  2. Re-list sessions via the store's list() so info comes from a fresh scan rather than a cached/old entry
  3. Reinstall or update Claude Code if its session format/location changed, then re-import the session
  4. Recreate the session if the underlying transcript file is gone — it cannot be loaded

Example fix

// before
const session = await store.load(staleInfo); // staleInfo from an old listing
// after
const sessions = await store.list();
const info = sessions.find(s => s.id === wantedId);
if (!info) throw new Error('session no longer available');
const session = await store.load(info);
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from "node:fs";
// before load:
// const stat = fs.statSync(info.path); // throws ENOENT/EACCES early
// if (stat.size === 0) throw new Error(`empty session file: ${info.path}`);

Type guard

function isMissingFileError(err: unknown): boolean {
  return err instanceof Error && (err as NodeJS.ErrnoException).code === 'ENOENT';
}

Try / catch

try {
  const session = await store.load(info);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Unable to read Claude session')) {
    logger.warn('claude session unreadable, skipping', { id: info.id, cause: err.message });
    return null; // degrade gracefully in session lists
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `claudeSessionStore.load(info)` where info.path points to a file that does not exist, is unreadable (permissions), is deleted between listing and load, or whose contents make collectForeignJsonRecords/fs.stat throw.

Common situations: Claude Code updated its session storage location or format so an old/stale entry in a session list no longer resolves; resuming a session after the ~/.claude/projects directory was cleaned or moved; running under a different user without read permission on the JSONL file.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/70095a2cbbcec4fc. Report an issue: GitHub.