Bash `sed -i` chained command silently emptied source file, wiping work
Model: Claude Opus 4.7 (model ID: claude-opus-4-7), 1M context, accessed via Claude Code.
Summary
A chained Bash command using sed -i '' (BSD/macOS variant) for a global rename inside a JavaScript source file resulted in the file being emptied (0 bytes) — losing several hours of in-progress work. The chained validation steps that followed (grep, node --check) reported success, masking the failure.
Approximate command shape
cd <dir> && sed -i '' 's/oldName/newName/g' <file.js> && grep -c "newName" <file.js> && grep -c "oldName" <file.js> && node --check <file.js> && echo "syntax ok"
The node --check passed because the empty file is technically valid JS, and the grep -c returns 0 (which is a non-zero exit code on no-match, short-circuiting the && chain after sed had already destroyed the file).
Outcome
~1700 lines of uncommitted source lost. Recovery was only possible because the user had not yet reloaded the unpacked Chrome extension that loaded the file — the source was still cached in Chrome DevTools' Sources panel under "Content scripts" and could be copy-pasted back into the empty file.
Suggested guardrails
- Default-deny destructive in-place editors (
sed -i,perl -i,gawk -i, etc.) in the harness, requiring explicit user opt-in. Configurable viapermissions.deny, but should ship as default-deny — the model has the Edit tool available and shouldn't reach for shell file rewrites. - Train the model to never use shell-based in-place file rewrites; always use the Edit tool for source modifications.
- Recommend frequent commits during long sessions (or surface a warning when uncommitted work crosses some threshold).
Workaround applied
User added the following to .claude/settings.local.json:
\\\json\
"permissions": {
"deny": [
"Bash(sed -i:*)",
"Bash(perl -i:*)",
"Bash(gawk -i:*)"
]
}
\\
Filed at user's request after this incident.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗