Claude Code Session Data Loss: Backup Script for Windows & Mac

Multiple users on r/ClaudeAI report that Claude Code silently loses session data — the session title remains in the sidebar, but clicking it reveals an empty transcript. No warning, no error, no recovery. This appears to happen during context compression, unexpected exits, or storage-layer issues.
The Backup Solution
A community-written script backs up all Claude Code data (sessions, projects, plans, drafts, memory) from ~/.claude to a separate folder, runs daily via Task Scheduler (Windows) or launchd (Mac), and retains 7 days of rolling backups.
Windows (PowerShell + Task Scheduler)
Create the backup folder:
mkdir C:\Users\%USERNAME%\ClaudeBackupsSave the following as backup-claude-sessions.ps1 in that folder:
$ErrorActionPreference = "Stop"
$source = "$env:USERPROFILE\.claude"
$backupRoot = "$env:USERPROFILE\ClaudeBackups"
$logFile = Join-Path $backupRoot "backup.log"
$keepDays = 7
$timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss"
$backupDir = Join-Path $backupRoot $timestamp
$dirs = @("sessions", "projects", "plans", "drafts", "memory")
function Write-Log($msg) {
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $msg"
Add-Content -Path $logFile -Value $line -Encoding utf8
}
try {
Write-Log "=== Backup started ==="
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
foreach ($d in $dirs) {
$src = Join-Path $source $d
if (Test-Path $src) {
$dst = Join-Path $backupDir $d
Copy-Item -Path $src -Destination $dst -Recurse -Force
$count = (Get-ChildItem $dst -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
Write-Log " Copied $d ($count files)"
} else {
Write-Log " Skipped $d (not found)"
}
}
$size = (Get-ChildItem $backupDir -Recurse -File | Measure-Object -Property Length -Sum).Sum
Write-Log " Total backup size: $([math]::Round($size/1MB, 2)) MB"
$cutoff = (Get-Date).AddDays(-$keepDays)
Get-ChildItem $backupRoot -Directory | Where-Object { $.Name -match '^\d{4}-\d{2}-\d{2}\d{6}$' -and $.CreationTime -lt $cutoff } | ForEach-Object {
Remove-Item $.FullName -Recurse -Force -Confirm:$false
Write-Log " Rotated old backup: $($.Name)"
}
Write-Log "=== Backup completed successfully ==="
} catch {
Write-Log "!!! BACKUP FAILED: $"
exit 1
}
Create install-schedule.ps1 and run as Administrator once:
$action = New-ScheduledTaskAction -Execute "powershell.exe"
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File "$env:USERPROFILE\ClaudeBackups\backup-claude-sessions.ps1""
$trigger = New-ScheduledTaskTrigger -Daily -At 8:00AM
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries
-DontStopIfGoingOnBatteries -StartWhenAvailable Register-ScheduledTask
-TaskName "ClaudeSessionsBackup" -Action $action
-Trigger $trigger -Settings $settings
-Description "Daily backup of Claude Code sessions" `
-RunLevel Limited
Write-Host "Done! Runs daily at 8:00 AM." -ForegroundColor GreenRun the installer:
powershell -ExecutionPolicy Bypass -File "C:\Users%USERNAME%\ClaudeBackups\install-schedule.ps1"Mac (launchd + shell script)
Create the backup folder:
mkdir -p /ClaudeBackupsSave as /ClaudeBackups/backup-claude-sessions.sh:
#!/bin/bash
set -euo pipefail
SOURCE="$HOME/.claude"
BACKUP_ROOT="$HOME/ClaudeBackups"
LOG_FILE="$BACKUP_ROOT/backup.log"
KEEP_DAYS=7
TIMESTAMP=$(date +"%Y-%m-%d_%H%M%S")
BACKUP_DIR="$BACKUP_ROOT/$TIMESTAMP"
DIRS=("sessions" "projects" "plans" "drafts" "memory")
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"; }
log "=== Backup started ==="
mkdir -p "$BACKUP_DIR"
for d in "${DIRS[@]}"; do
src="$SOURCE/$d"
if [ -d "$src" ]; then
cp -R "$src" "$BACKUP_DIR/$d"
count=$(find "$BACKUP_DIR/$d" -type f | wc -l | tr -d ' ')
log " Copied $d ($count files)"
else
log " Skipped $d (not found)"
fi
done
size=$(du -sb "$BACKUP_DIR" | cut -f1)
log " Total backup size: $(echo "scale=2; $size/1048576" | bc) MB"
find "$BACKUP_ROOT" -maxdepth 1 -type d -name "????-??-??_??????" -mtime +$KEEP_DAYS -exec rm -rf {} + -exec log " Rotated old backup: {}" ;
log "=== Backup completed successfully ==="Make executable and schedule via launchd (see original post for plist setup).
The script runs completely independently of Claude Code, so even if Claude crashes or loses data, backups are preserved. It logs each run to backup.log for verification.
📖 Read the full source: r/ClaudeAI
👀 See Also

LAP: 1,500+ API Specs Compiled for LLM Consumption to Reduce Claude Hallucinations
LAP is a tool that compiles 1,500+ real API specifications into a lean format optimized for LLMs, providing verified endpoints and parameters to prevent AI coding agents like Claude from hallucinating incorrect API calls.

Testing Local LLMs for Autonomous Code Generation: Quality vs. Speed Benchmark
A developer built a harness to test local LLMs on real Go code generation tasks, measuring compilation success, field extraction accuracy, and throughput. Results compare models across quality and speed.

Snip tool enables visual communication with AI coding agents
Snip is a free tool that lets developers screenshot, annotate, and draw to show AI agents what they mean visually, while agents can generate diagrams or load images directly through CLI or MCP. Currently runs on Apple Silicon Macs with Mermaid diagram support and HTML support in progress.

Shipshots MCP Server: Claude Designs App Store Screenshots and Preview Videos
Shipshots is a visual editor with an MCP server that lets Claude design marketing materials through tool calls. It generates app store screenshots, animated preview videos, and social media visuals based on text descriptions.