Obsidian Incident Runbook
Overview
Systematic procedures for diagnosing and resolving Obsidian failures: plugin crashes, vault corruption, sync conflicts, performance degradation, and broken CSS/themes. Each section is a self-contained runbook -- jump to the relevant one.
Prerequisites
- Access to the affected Obsidian vault directory
- Developer Console access (Ctrl+Shift+I / Cmd+Option+I)
- Terminal access for filesystem operations
- Backup awareness (know where your backups are before making changes)
Instructions
Step 1: Quick Triage
Determine the category before diving in:
| Symptom | Category | Go to | |---------|----------|-------| | Obsidian crashes on open or plugin enable | Plugin crash | Step 2 | | Notes missing, corrupted, or garbled | Vault corruption | Step 3 | | Duplicate files, conflicting edits | Sync conflicts | Step 4 | | Obsidian slow, high CPU/memory, lag while typing | Performance | Step 5 | | UI elements missing, wrong colors, broken layout | CSS/Theme | Step 6 |
Step 2: Plugin Crash Recovery
Immediate: Enter Safe Mode
If Obsidian crashes on startup, force safe mode:
VAULT_PATH=~/path/to/vault
# Option A: Disable all community plugins
echo '[]' > "$VAULT_PATH/.obsidian/community-plugins.json"
# Option B: Disable a specific suspect plugin
python3 -c "
import json
plugins = json.load(open('$VAULT_PATH/.obsidian/community-plugins.json'))
suspect = 'plugin-id-here'
if suspect in plugins:
plugins.remove(suspect)
json.dump(plugins, open('$VAULT_PATH/.obsidian/community-plugins.json', 'w'))
print(f'Disabled {suspect}')
else:
print(f'{suspect} not in active plugins')
"
Reopen Obsidian. If it loads, the disabled plugin was the cause.
Diagnose from Console
Open Developer Console (Ctrl+Shift+I) and look for errors:
// Check for plugin load failures
Object.entries(app.plugins.manifests).forEach(([id, manifest]) => {
const loaded = app.plugins.plugins[id];
if (!loaded) console.error(`FAILED TO LOAD: ${id} v${manifest.version}`);
});
// Check for unhandled rejections in recent history
// (must be open before reproducing the issue)
window.addEventListener('unhandledrejection', (e) => {
console.error('Unhandled rejection:', e.reason);
});
Binary search for the offending plugin
If multiple plugins could be the cause:
VAULT_PATH=~/path/to/vault
PLUGINS_FILE="$VAULT_PATH/.obsidian/community-plugins.json"
# Save original list
cp "$PLUGINS_FILE" "$PLUGINS_FILE.bak"
# Get all plugins
python3 -c "
import json
plugins = json.load(open('$PLUGINS_FILE'))
half = len(plugins) // 2
# Enable only first half
json.dump(plugins[:half], open('$PLUGINS_FILE', 'w'))
print(f'Enabled {half} of {len(plugins)} plugins: {plugins[:half]}')
print(f'Disabled: {plugins[half:]}')
print('Open Obsidian. If it crashes, problem is in first half. If stable, second half.')
"
Repeat halving until you isolate the single offending plugin.
Step 3: Vault Corruption Recovery
Check for corrupted files
VAULT_PATH=~/path/to/vault
# Find files with null bytes (corruption indicator)
echo "=== Files with null bytes ==="
find "$VAULT_PATH" -name '*.md' -not -path '*/.obsidian/*' -exec grep -Pl '\x00' {} \;
# Find zero-byte files (likely lost content)
echo "=== Empty files ==="
find "$VAULT_PATH" -name '*.md' -not -path '*/.obsidian/*' -empty
# Find files with broken YAML frontmatter
echo "=== Broken frontmatter ==="
for f in "$VAULT_PATH"/*.md "$VAULT_PATH"/**/*.md; do
[ -f "$f" ] || continue
if head -1 "$f" | grep -q '^---' && ! awk '/^---/{c++; if(c==2) exit 0} END{exit (c<2)}' "$f"; then
echo " Unclosed frontmatter: $f"
fi
done
Recover from .trash
Obsidian moves deleted files to .trash/ in the vault:
# List recently deleted files
ls -lt "$VAULT_PATH/.trash/" 2>/dev/null | head -20
# Restore a specific file
cp "$VAULT_PATH/.trash/important-note.md" "$VAULT_PATH/recovered/"
Recover from Git backup
If the vault is under Git version control:
cd "$VAULT_PATH"
# See what changed recently
git log --oneline -20
git diff HEAD~1 --stat
# Restore a specific file to its last good state
git checkout HEAD~1 -- "path/to/corrupted-note.md"
# Restore the entire vault to last commit (DESTRUCTIVE -- stash first)
git stash
git checkout HEAD -- .
Recover from filesystem snapshots
# macOS Time Machine
tmutil listbackups 2>/dev/null | tail -5
# Then browse: /Volumes/TimeMachine/Backups.backupdb/.../path/to/vault
# Linux (btrfs snapshots)
ls /.snapshots/ 2>/dev/null
Step 4: Sync Conflict Resolution
Obsidian Sync conflicts
Obsidian Sync creates conflict files named Note (conflict YYYY-MM-DD).md:
VAULT_PATH=~/path/to/vault
# Find all conflict files
echo "=== Sync Conflicts ==="
find "$VAULT_PATH" -name '*conflict*' -not -path '*/.obsidian/*'
# Compare a conflict with its original
ORIGINAL="$VAULT_PATH/Meeting Notes.md"
CONFLICT=$(find "$VAULT_PATH" -name "Meeting Notes*conflict*" | head -1)
if [ -n "$CONFLICT" ]; then
diff "$ORIGINAL" "$CONFLICT"
fi
Resolution: Open both files in Obsidian, manually merge content into the original, delete the conflict file.
Git sync conflicts
cd "$VAULT_PATH"
# Check for merge conflicts
git status | grep 'both modified'
# For each conflicted file, resolve the conflict markers
# <<<<<<< HEAD
# (your changes)
# =======
# (their changes)
# >>>>>>> branch
grep -rl '<<<<<<< ' "$VAULT_PATH"/*.md 2>/dev/null
Prevent future conflicts
In .obsidian/sync.json or your Git workflow:
- Exclude
workspace.jsonandworkspace-mobile.jsonfrom sync (per-device files) - Avoid editing the same note on two devices simultaneously
- For Git: commit and push frequently; pull before editing
Step 5: Performance Degradation
Diagnose the bottleneck
// Paste in Developer Console
// Check memory usage
console.log('Memory:', JSON.stringify(performance.memory, null, 2));
// Time plugin load
Object.entries(app.plugins.plugins).forEach(([id, plugin]) => {
const start = performance.now();
// Plugins are already loaded, but check their event listener count
const events = plugin._events?.length || 0;
console.log(`${id}: ${events} event listeners`);
});
// Check for expensive metadata cache operations
console.time('metadataCache');
const allFiles = app.vault.getMarkdownFiles();
allFiles.forEach(f => app.metadataCache.getFileCache(f));
console.timeEnd('metadataCache');
console.log(`Files scanned: ${allFiles.length}`);
Disable plugins one by one
Systematic approach to find the performance culprit:
VAULT_PATH=~/path/to/vault
PLUGINS_FILE="$VAULT_PATH/.obsidian/community-plugins.json"
# Save original
cp "$PLUGINS_FILE" "$PLUGINS_FILE.bak"
# Get plugin list
python3 -c "
import json
plugins = json.load(open('$PLUGINS_FILE'))
print('Current plugins:')
for i, p in enumerate(plugins):
print(f' {i}: {p}')
print(f'\nTotal: {len(plugins)} plugins')
print('\nTo disable one at a time:')
for p in plugins:
without = [x for x in plugins if x != p]
print(f' Without {p}: {len(without)} remaining')
"
Then for each suspect:
- Remove it from
community-plugins.json - Restart Obsidian
- Test performance
- If improved, that plugin is the bottleneck
- If unchanged, restore it and try the next
Common performance fixes
- Vault with 10,000+ files: disable Dataview's automatic refresh, use lazy loading
- Many backlinks: disable backlinks panel or set it to collapsed by default
- Large files (1MB+): split into smaller notes using note refactoring
- Too many plugins (20+): audit and remove unused plugins
Step 6: CSS and Theme Issues
Nuclear option: Reset all custom CSS
VAULT_PATH=~/path/to/vault
# Disable all CSS snippets
python3 -c "
import json
try:
a = json.load(open('$VAULT_PATH/.obsidian/appearance.json'))
a['enabledCssSnippets'] = []
a['cssTheme'] = '' # Reset to default theme
json.dump(a, open('$VAULT_PATH/.obsidian/appearance.json', 'w'), indent=2)
print('Reset theme and disabled all snippets')
except Exception as e:
print(f'Error: {e}')
"
Restart Obsidian. If the UI is fixed, re-enable snippets and theme one at a time.
Check for CSS conflicts
VAULT_PATH=~/path/to/vault
# Find snippets with aggressive selectors
for snippet in "$VAULT_PATH/.obsidian/snippets"/*.css; do
[ -f "$snippet" ] || continue
name=$(basename "$snippet")
important_count=$(grep -c '!important' "$snippet" 2>/dev/null)
if [ "$important_count" -gt 0 ]; then
echo "$name: $important_count !important declarations"
fi
done
# Check theme compatibility with current Obsidian version
THEME_DIR="$VAULT_PATH/.obsidian/themes"
for theme in "$THEME_DIR"/*/manifest.json; do
[ -f "$theme" ] || continue
python3 -c "
import json
m = json.load(open('$theme'))
print(f\"{m.get('name')}: minAppVersion={m.get('minAppVersion', 'unspecified')}\")
"
done
Diagnose specific CSS problems
In Developer Console:
// Find which CSS rule is affecting a specific element
// Right-click the broken element -> Inspect
// In the Elements panel, check Computed styles and look for overrides
// Programmatically check for theme variable conflicts
const root = getComputedStyle(document.body);
const vars = [
'--background-primary', '--background-secondary',
'--text-normal', '--text-accent',
'--interactive-accent', '--interactive-hover'
];
vars.forEach(v => console.log(`${v}: ${root.getPropertyValue(v)}`));
Output
- Identified root cause of the incident
- Applied fix (plugin disabled, file recovered, conflict resolved, CSS reset)
- Documented what happened for future reference
- Preventive measures configured (backups, sync exclusions, performance monitoring)
Error Handling
| Issue | Cause | Quick Fix |
|-------|-------|-----------|
| Console won't open (Obsidian crashes immediately) | Plugin error in onload() | Disable all plugins via filesystem (Step 2) |
| Can't access vault directory | Filesystem permissions | chmod -R u+rw "$VAULT_PATH" |
| Plugin won't disable via community-plugins.json | File locked by sync | Stop sync service, edit file, restart |
| Obsidian hangs (not crashing, just frozen) | Infinite loop in plugin | Force-quit (kill process), then disable suspect plugin |
| Safe mode still crashes | Core Obsidian issue, not plugins | Reinstall Obsidian; vault data is separate from app |
| Recovery file also corrupted | Filesystem-level issue | Check disk health (fsck, diskutil, chkdsk) |
Examples
Plugin crash on startup: User reports Obsidian instantly closes after opening a vault. Enter safe mode by clearing community-plugins.json (Step 2). Reopen works. Binary search reveals obsidian-excalidraw conflicts with a newly installed obsidian-kanban. Disable one, report the conflict to both plugin authors.
Missing notes after sync: User opens vault on laptop, 20 notes are gone. Check .trash/ -- empty. Check Git log -- notes were deleted in a commit from their phone. git checkout HEAD~3 -- missing-folder/ restores them. Add the folder to selective sync to prevent mobile edits.
Slow vault with 8,000 notes: Obsidian takes 15 seconds to open and typing lags. Developer Console shows metadataCache taking 4 seconds. Disabling Dataview drops it to 1 second. Solution: configure Dataview to use manual refresh instead of auto-refresh, and add dv.pages limits to expensive queries.
Resources
- Obsidian Forum - Bug Reports
- Obsidian Discord - Help
- Plugin Developer Documentation
- Obsidian Safe Mode
- Obsidian Sync Troubleshooting
Next Steps
For collecting detailed diagnostics to share with plugin developers, see obsidian-debug-bundle. For data backup and recovery strategies, see obsidian-data-handling.