You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Running UNDO command deletes a file that was just read #46783
OK, this is a bit hard to explain, but hear me out. Running a /undo command deletes a file under very specific circumstances. Seems like strange /undo logic.
I asked LLM to read some file that I downloaded, which failed because I forgot to move it from ~/Downloads into my current working directory. OK I think, no big deal, I move the file over, run /undo in OpenCode and run the prompt again - but it fails again saying. So I got very confused and indeed the file vanished.
It took me a while to figure out what is going on, but it seems to be easily reproducible and reliable bug.
Plugins
None
OpenCode version
1.18.18
Steps to reproduce
ask LLM to read a non-existing file (it fails obviously)
create the file
run /undo so that you can re-run the prompt
the file is suddenly gone because of the /undo command
Reproducible for a structural reason, I think. Reading the revert path on main today:
packages/core/src/session/revert.ts, in plan(), builds the restore map by taking every path a message recorded and pointing it at that message's start tree:
stage() then hands that map straight to snapshot.restore({ files: restore }). And restore's documented contract in packages/core/src/snapshot.ts is:
Restore selected project-relative paths from their associated trees. A path absent from its selected tree is removed; paths outside the map are untouched.
So the deletion isn't an edge case in the diff logic, it's the specified behaviour: a path in the map that does not exist in its tree gets removed. In this report the path was recorded by the message, the file did not exist at that message's start, and so restoring it means deleting it.
The part I could not confirm from reading alone is the first link in that chain: whether a failed read is what puts the path into message.snapshot.files. That would explain the reproduction exactly, but someone who knows the tool-result recording path should check it rather than take my word for it.
Worth noting the blast radius is wider than the title suggests, if the above is right. The rule "reset these paths to their pre-message state" deletes anything now living at a recorded path that wasn't there at start — regardless of who put it there. The reporter created the file by hand between the failed read and the /undo, and it was still removed. Any file a human creates at a path the agent previously touched is exposed to the same thing.
The distinction that seems to be missing is between this path was mentioned and this path was written by the turn being undone. Only the second is something an undo has any business deleting. A guard along the lines of "only remove a path if the turn actually created it" (rather than "if it is absent from the start tree") would close the reported case without changing legitimate undos, since a path the turn genuinely created is absent from startand was written by that turn, while this one is only the former.
Activity
mahirhir commentedon Sep 2, 2026
Reproducible for a structural reason, I think. Reading the revert path on
maintoday:packages/core/src/session/revert.ts, inplan(), builds the restore map by taking every path a message recorded and pointing it at that message's start tree:stage()then hands that map straight tosnapshot.restore({ files: restore }). Andrestore's documented contract inpackages/core/src/snapshot.tsis:So the deletion isn't an edge case in the diff logic, it's the specified behaviour: a path in the map that does not exist in its tree gets removed. In this report the path was recorded by the message, the file did not exist at that message's
start, and so restoring it means deleting it.The part I could not confirm from reading alone is the first link in that chain: whether a failed read is what puts the path into
message.snapshot.files. That would explain the reproduction exactly, but someone who knows the tool-result recording path should check it rather than take my word for it.Worth noting the blast radius is wider than the title suggests, if the above is right. The rule "reset these paths to their pre-message state" deletes anything now living at a recorded path that wasn't there at
start— regardless of who put it there. The reporter created the file by hand between the failed read and the/undo, and it was still removed. Any file a human creates at a path the agent previously touched is exposed to the same thing.The distinction that seems to be missing is between this path was mentioned and this path was written by the turn being undone. Only the second is something an undo has any business deleting. A guard along the lines of "only remove a path if the turn actually created it" (rather than "if it is absent from the start tree") would close the reported case without changing legitimate undos, since a path the turn genuinely created is absent from
startand was written by that turn, while this one is only the former.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊
AtomicFS commentedon Oct 12, 2026
I have done a quick test on version
v2.0.26and it seems to have been fixed.