{"record":{"id":"404f2474092e888d","repo":"can1357/oh-my-pi","slug":"unable-to-read-codex-session-info-id-at-info","errorCode":null,"errorMessage":"Unable to read Codex session ${info.id} at ${info.path}","messagePattern":"Unable to read Codex session (.+?) at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/codex-session-store.ts","lineNumber":549,"sourceCode":"\t\t\t\ttitle: indexed?.thread_name,\n\t\t\t\tcreated,\n\t\t\t\tmodified,\n\t\t\t});\n\t\t}\n\t\tsessions.sort(\n\t\t\t(left, right) => right.modified.getTime() - left.modified.getTime() || left.id.localeCompare(right.id),\n\t\t);\n\t\treturn sessions;\n\t}\n\n\t/** Converts one Codex rollout into a non-persistent OMP session. */\n\tasync load(info: ForeignSessionInfo): Promise<SessionManager> {\n\t\tif (info.source !== \"codex\") throw new Error(`Cannot load ${info.source} session with CodexSessionStore`);\n\t\tlet records: Record<string, unknown>[];\n\t\ttry {\n\t\t\trecords = await readJsonLines(info.path);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Unable to read Codex session ${info.id} at ${info.path}`, { cause: error });\n\t\t}\n\t\tif (records.length === 0) throw new Error(`Codex session ${info.id} at ${info.path} is empty or malformed`);\n\n\t\tconst metadata = records.find(record => record.type === \"session_meta\" && isRecord(record.payload));\n\t\tconst cwd =\n\t\t\tmetadata && isRecord(metadata.payload) ? (stringField(metadata.payload, \"cwd\") ?? info.cwd) : info.cwd;\n\t\tconst manager = SessionManager.inMemory(cwd);\n\t\tconst canonical = canonicalTexts(records);\n\t\tconst converted: ConvertedRecord[] = [];\n\t\tconst toolNames = new Map<string, string>();\n\t\tlet model = \"codex\";\n\t\tlet fallbackTimestamp = info.created.getTime();\n\t\tlet title = info.title;\n\n\t\tfor (const record of records) {\n\t\t\tconst timestamp = timestampMillis(record.timestamp, fallbackTimestamp);\n\t\t\tfallbackTimestamp = Math.max(fallbackTimestamp + 1, timestamp);\n\t\t\tif (!isRecord(record.payload)) continue;","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/codex-session-store.ts#L531-L567","documentation":"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.).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect error.cause to see the real failure (ENOENT vs permission vs JSON parse)","Confirm the file at info.path exists and each line parses as JSON (head path)","Re-list Codex sessions to get fresh paths after a Codex upgrade or directory move","Restore the rollout file from backup or abandon the session"],"exampleFix":"// before\ntry { await codexStore.load(info); } catch (e) { console.log(e.message); }\n// after\ntry { await codexStore.load(info); } catch (e) {\n  console.error(e.message, 'cause:', e.cause);\n  const fresh = await codexStore.list();\n  if (!fresh.some(s => s.id === info.id)) console.error('session file gone; re-list');\n}","handlingStrategy":"try-catch","validationCode":"import * as fs from \"node:fs\";\n// before load:\n// if (!fs.existsSync(info.path)) throw new Error(`rollout gone: ${info.path}`);\n// probe JSONL: first non-empty line must JSON.parse","typeGuard":"function isUnreadableCodexSession(err: unknown): boolean {\n  return err instanceof Error && err.message.startsWith('Unable to read Codex session');\n}","tryCatchPattern":"try {\n  const manager = await codexStore.load(info);\n} catch (err) {\n  if (isUnreadableCodexSession(err)) {\n    logger.warn('codex rollout unreadable', { path: info.path, cause: String(err.cause) });\n    return null;\n  }\n  throw err;\n}","preventionTips":["Always inspect err.cause — this error wraps the real filesystem/JSON failure","Re-list sessions after any Codex CLI upgrade; paths can change","Treat missing rollout files as normal (sessions get cleaned up) in resuming UIs","Avoid hand-copying rollout files; move them with the directory structure intact"],"tags":["session","filesystem","io","codex"],"backgroundTag":"session-file-unreadable","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}