Skip to content

Running UNDO command deletes a file that was just read #46783

Closed as not planned
Closed as not planned

Description

@AtomicFS

Description

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

Screenshot and/or share link

No response

Operating System

ArchLinux

Terminal

foot

Activity

  1. mahirhir commented on Sep 2, 2026

    @mahirhir

    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:

    for (const file of message.snapshot.files ?? [])
      if (!files.has(file)) files.set(file, Snapshot.ID.make(message.snapshot.start))

    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 start and was written by that turn, while this one is only the former.

  2. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  3. AtomicFS commented on Oct 12, 2026

    @AtomicFS
    Author

    I have done a quick test on version v2.0.26 and it seems to have been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions