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

NexQuant: Rust-native 3-bit KV-cache engine for edge deployment
NexQuant is a production-hardened Rust engine that enables running high-context models on consumer hardware with 3-5x memory reduction. It supports Metal, CUDA, Vulkan, and CPU backends.

OpenClaw skill adds AI image generation with local ComfyUI support and curated prompts
A new OpenClaw skill provides AI image generation capabilities directly in the terminal, featuring 1,300+ curated prompts, local ComfyUI integration, and prompt enhancement workflows.

Open-source SwiftUI testing skill for Claude Code uses Computer Use to visually test apps
An open-source Claude Code skill called /ios-test visually tests SwiftUI apps using Computer Use capability. The agent finds .xcodeproj files, builds the app in a Simulator, then navigates through every screen, tapping buttons and following links like a real user.

Inline Visualizer: Local AI Models Can Now Render Interactive HTML Visualizations
Inline Visualizer is a BSD-3 licensed plugin for Open WebUI that enables any local AI model with tool calling support to render interactive HTML/SVG visualizations directly in chat, with a JavaScript bridge allowing elements to send messages back to the AI.