Skip to main content
Claude Code persists every session as an append-only JSONL log. This lets you resume a previous conversation exactly where you left off, fork a session into a new branch, or recover from crashes without losing work.

Storage Location

Sessions are stored under ~/.claude/projects/:
~/.claude/projects/<hash>/sessions/
└── <session-id>.jsonl    ← append-only log
The <hash> is derived from the working directory path, so each project directory gets its own session namespace. Session IDs use the format <prefix><8 characters>.

JSONL Format

Each line in the .jsonl file is a self-contained JSON object. Four entry types are written:
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Refactor this function"}]},"sessionId":"abc12345","cwd":"/home/user/myproject","timestamp":"2024-01-15T10:30:00.000Z"}
{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"I'll start by reading the file..."}]},"sessionId":"abc12345","costUSD":0.0012}
{"type":"progress","toolName":"FileReadTool","toolInput":{"path":"src/utils.ts"},"sessionId":"abc12345"}
{"type":"system","subtype":"compact_boundary","summary":"The assistant read utils.ts and identified three functions to refactor...","sessionId":"abc12345"}
Entry typeWhen writtenNotes
userOn every user messageWritten with await (blocking) for crash recovery
assistantOn every assistant messageWritten fire-and-forget (order-preserving queue)
progressDuring tool executionInline write; deduplicated on next query
system/compact_boundaryAfter autoCompact runsMarks where summarization occurred

Persistence Strategy

Different message types use different write strategies, balancing durability against performance:
User messages  ──> await write      (blocking — ensures user input survives crashes)
Assistant msgs ──> fire-and-forget  (order-preserving queue — non-blocking)
Progress       ──> inline write     (dedup on next query to avoid duplicate entries)
Flush          ──> on result yield / cowork eager flush
The fire-and-forget queue in src/utils/sessionStorage.ts (recordTranscript()) guarantees ordering without blocking the agent loop. This is the Fire-and-Forget Write design pattern — a background queue drains writes while the agent continues processing.

Resume Flow

1

Locate the session log

getLastSessionLog() scans ~/.claude/projects/<hash>/sessions/ and returns the most recently modified .jsonl file, or the file matching the --resume <id> argument.
2

Parse the JSONL

Each line is parsed into its typed entry. Invalid or truncated lines (from a crash mid-write) are skipped.
3

Rebuild messages[]

user and assistant entries are converted back into MessageParam objects and pushed into the messages[] array in order. The compact_boundary entry signals that older messages have already been summarized.
4

Restore compact boundary

If a compact_boundary entry exists, messages[] is rebuilt starting from that boundary — the summarized history before it is prepended as a single assistant message containing the compact summary.
5

Continue the session

QueryEngine.submitMessage() is called with the next user prompt. The rebuilt messages[] provides full conversation context, and the session ID is preserved for continued JSONL appending.

CLI Flags

FlagBehavior
--continueResumes the last session associated with the current working directory
--resume <id>Resumes a specific session by its session ID
--fork-sessionCreates a new session ID but copies the history from the last session — diverges from this point forward

/resume Slash Command

Within an interactive session, /resume lists recent sessions and lets you jump to one without restarting the CLI:
/resume                    ← lists recent sessions for current project
/resume abc12345           ← jumps directly to session abc12345
The command reads from ~/.claude/projects/<hash>/sessions/, displays a summary of each session (start time, message count, last message preview), and replaces the current messages[] with the selected session’s history.

Session ID Format

Session IDs follow the pattern <prefix><8 alphanumeric characters>. The prefix encodes the session type:
PrefixType
bBash task
aAgent task
rRemote agent task
tIn-process teammate task
(none)Main interactive session
Example: a_f3a9b2c1 is an agent sub-task session.

Crash Recovery

Because user messages are written with await (blocking), a crash during tool execution will always have the user’s last message in the JSONL log. On resume, the rebuilt messages[] will include that user message, and the agent will re-respond from that point — tools that were interrupted mid-execution will be re-run.
If a tool with side effects (e.g., FileEditTool, BashTool) was mid-execution at crash time, it may run again on resume. Claude Code does not implement idempotency tracking for tool re-execution after crash recovery.

Build docs developers (and LLMs) love