can1357/oh-my-pi · error · Error

Unable to read Codex session ${info.id} at ${info.path}

Error message

Unable to read Codex session ${info.id} at ${info.path}

What it means

CodexSessionStore.load() failed to read/parse the Codex rollout file (readJsonLines threw), and rethrows under a message naming the session id and path with the original error attached as `cause`. Check error.cause for the underlying reason (ENOENT, EACCES, JSON syntax error, etc.).

Source

Thrown at packages/coding-agent/src/session/codex-session-store.ts:549

				title: indexed?.thread_name,
				created,
				modified,
			});
		}
		sessions.sort(
			(left, right) => right.modified.getTime() - left.modified.getTime() || left.id.localeCompare(right.id),
		);
		return sessions;
	}

	/** Converts one Codex rollout into a non-persistent OMP session. */
	async load(info: ForeignSessionInfo): Promise<SessionManager> {
		if (info.source !== "codex") throw new Error(`Cannot load ${info.source} session with CodexSessionStore`);
		let records: Record<string, unknown>[];
		try {
			records = await readJsonLines(info.path);
		} catch (error) {
			throw new Error(`Unable to read Codex session ${info.id} at ${info.path}`, { cause: error });
		}
		if (records.length === 0) throw new Error(`Codex session ${info.id} at ${info.path} is empty or malformed`);

		const metadata = records.find(record => record.type === "session_meta" && isRecord(record.payload));
		const cwd =
			metadata && isRecord(metadata.payload) ? (stringField(metadata.payload, "cwd") ?? info.cwd) : info.cwd;
		const manager = SessionManager.inMemory(cwd);
		const canonical = canonicalTexts(records);
		const converted: ConvertedRecord[] = [];
		const toolNames = new Map<string, string>();
		let model = "codex";
		let fallbackTimestamp = info.created.getTime();
		let title = info.title;

		for (const record of records) {
			const timestamp = timestampMillis(record.timestamp, fallbackTimestamp);
			fallbackTimestamp = Math.max(fallbackTimestamp + 1, timestamp);
			if (!isRecord(record.payload)) continue;

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect error.cause to see the real failure (ENOENT vs permission vs JSON parse)
  2. Confirm the file at info.path exists and each line parses as JSON (head path)
  3. Re-list Codex sessions to get fresh paths after a Codex upgrade or directory move
  4. Restore the rollout file from backup or abandon the session

Example fix

// before
try { await codexStore.load(info); } catch (e) { console.log(e.message); }
// after
try { await codexStore.load(info); } catch (e) {
  console.error(e.message, 'cause:', e.cause);
  const fresh = await codexStore.list();
  if (!fresh.some(s => s.id === info.id)) console.error('session file gone; re-list');
}
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from "node:fs";
// before load:
// if (!fs.existsSync(info.path)) throw new Error(`rollout gone: ${info.path}`);
// probe JSONL: first non-empty line must JSON.parse

Type guard

function isUnreadableCodexSession(err: unknown): boolean {
  return err instanceof Error && err.message.startsWith('Unable to read Codex session');
}

Try / catch

try {
  const manager = await codexStore.load(info);
} catch (err) {
  if (isUnreadableCodexSession(err)) {
    logger.warn('codex rollout unreadable', { path: info.path, cause: String(err.cause) });
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `codexSessionStore.load(info)` where the rollout file at info.path is missing, unreadable, or contains a line that is not valid JSON — anything that makes readJsonLines reject.

Common situations: Codex CLI moved/renamed its session directory on upgrade so stale listings point at deleted files; partially-written rollout from a crashed Codex run; permissions changed after running under a different user; hand-copied session file is truncated.

Related errors


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