Skip to content

/undo destroyed 227 files, /restore destroyed 15,232 files. Several HUNDREDS OF THOUSANDS OF DOLLARS OF DAMAGE #53560

Closed as not planned

Description

@lasertiger

Description

User tried /undo to remove last command run. That did far more than was asked and 227 files were changed. User tried /restore and that changed 15,232 files overwriting entire projects and destroying work across entire monorepo.

In OpenCode 1.18.34 on macOS, an undo/redo interaction caused project-wide filesystem writes in my monorepo, despite the affected conversation having zero recorded file-change patches.

The UI displayed 227 files with 46,743 removed text lines. The underlying restore actually rewrote 15,232 files, including files unrelated to the conversation. I had substantial uncommitted work created through other tools and Codex sessions.

Environment and sequence

  • OpenCode 1.18.34, confirmed in installed package metadata and session records.
  • macOS; OpenCode session opened at the monorepo root.
  • Incident: October 3, 2026, approximately 19:47–19:54 UTC.
  • I invoked /undo, saw several screens of apparent deletions, and subsequently attempted redo. The logs confirm an unreverting operation.
  • The retained sequence contains multiple undo/redo state transitions and nine whole-tree restore starts. I cannot reconstruct every keypress; I am not claiming one keypress caused nine restores.

Confirmed findings

An investigation of the SQLite database, historical events, logs, snapshots and current files established:

  1. The affected session has zero patch parts in both retained message records and historical events. Its two attempted write-tool calls failed/interrupted.
  2. The logs repeatedly report removing gitignored files from snapshot with a count of 227. The stored undo diff reports those paths as deleted, with 46,743 removed text lines and four binary entries.
  3. The restore tree contains 15,232 file blobs and two submodule pointers. All 15,232 files have modification times within the final restore window, 19:53:39.141–19:54:02.139 UTC, and their current content hashes match that tree.
  4. The final historical session event clears the revert state.

Relevant implementation

In the [v1.18.34 session revert implementation (https://github.com/anomalyco/opencode/blob/aec0b9a6d8898f68f923aaf08b7306d931fd9d76/packages/opencode/src/session/revert.ts)](https://github.com/anomalyco/opencode/blob/aec0b9a6d8898f68f923aaf08b7306d931fd9d76/packages/opencode/src/session/revert.ts), subsequent revert operations restore the existing saved tree before processing selected patches. That restore is not conditional on the patch list being nonempty. unrevert also restores the saved tree.

The [snapshot implementation (https://github.com/anomalyco/opencode/blob/aec0b9a6d8898f68f923aaf08b7306d931fd9d76/packages/opencode/src/snapshot/index.ts)](https://github.com/anomalyco/opencode/blob/aec0b9a6d8898f68f923aaf08b7306d931fd9d76/packages/opencode/src/snapshot/index.ts) uses read-tree followed by checkout-index -a -f, forcibly writing every indexed file—including unrelated files and files whose contents are already identical.

Separately, ignored-file cleanup removes entries from the internal snapshot index, and the cached diff can present those removals as filesystem deletions. The displayed scope and actual write scope are therefore very different.

Impact and evidence limits

The broad overwrite is confirmed.

OpenCode retained a snapshot when undo started. The concern is that subsequent restores overwrite working files without preserving intervening contents or checking for changes made by another session/tool. No snapshot add/list failure was found around this incident.

Expected behavior

  • Undo/redo with no session file patches should not rewrite thousands of working files.
  • Restoration should be limited to affected changes and detect conflicting newer edits before overwriting them.
  • Ignored-file bookkeeping should not appear as deletion of working files.
  • The interface should accurately describe the files it will write and whether restoration completed.

Please add regression coverage for an empty-patch session in a dirty worktree, edits made by another tool between undo and redo, and ignored entries present in an earlier snapshot. A minimal end-to-end reproduction of the entire incident has not yet been established.

Also please:

  1. No recorded file changes means no filesystem writes.
    If the selected conversation operations contain no file changes, undo may change conversation state only.
  2. Restrict writes to explicitly recorded changes.
    Only files changed by the selected operations are eligible. Reading a file, including it in a snapshot, or finding unrelated differences elsewhere does not authorize changing it.
  3. Require the expected current contents.
    If an operation changed a file from A to B, undo may replace B with A. If the file now contains C, stop and report a conflict. Apply the corresponding check to redo and restore.
  4. Leave identical files untouched.
    If the current contents already equal the desired contents, perform no write and preserve the modification time.
  5. Build one exact operation plan.
    Identify every file to create, overwrite, delete or rename. Preview and execution must use that same plan. Do not display a narrow diff and then execute a broader whole-tree restore.
  6. Prepare recovery for the entire operation before its first destructive write.
    Preserve the actual current contents of every file that could be overwritten or deleted. Record original paths, file types, relevant permissions and whether each target existed. The snapshot being restored is replacement data; it does not substitute for this recovery copy.
  7. Verify recovery copies and fail closed.
    Check that the preserved contents are complete, readable and match the originals. If capture, storage or verification fails, perform no destructive writes. Never silently proceed using an older snapshot.
  8. Protect recovery material from the operation itself.
    Store it outside the restore’s targets. It must not depend on objects that ordinary cleanup could prune. Keep it throughout execution, failure handling and the promised recovery period. Clearly communicate when recovery will expire.
  9. Keep a durable operation journal.
    Record the intended changes, recovery locations and enough per-file progress to determine what happened after a crash. A success message in memory is insufficient.
  10. Make partial failure recoverable.
    If restoration fails halfway, stop further destructive work and retain all recovery material. Report what completed, what failed and what remains uncertain. Provide a way to inspect, resume or reverse the partial operation.
  11. Protect against rollback failure too.
    Rollback must obey the same scope, conflict and preservation rules. If rollback fails, keep the copies and journal usable for manual recovery. Never discard the recovery data merely because recovery was attempted.
  12. Prevent overlapping operations and recheck for intervening edits.
    Serialize undo/redo/restore operations across the shared worktree, not just within one conversation. Revalidate files before writing. An application lock does not establish that an editor or another tool left the files unchanged.
  13. Reject disproportionate scope.
    An operation recorded as changing two files cannot legitimately write 200 files. Add configurable file-count, byte-count and deletion thresholds as extra guards. Passing a threshold—or accepting a generic confirmation—must not bypass scope checks, conflict checks or recovery preparation.
  14. Report actual filesystem effects accurately.
    Distinguish writes, deletions, unchanged files, conflicts and internal bookkeeping. Removing an entry from a snapshot index must not appear as deleting a working file. State clearly whether an operation completed, partially completed or failed, and where its recovery material resides.
  15. Test the failure paths explicitly.
    Cover empty patch lists, unrelated uncommitted work, ignored files, edits between undo and redo, failed snapshot capture, insufficient disk space, interruption halfway through restore, and rollback failure. Check both preservation of unrelated contents and usability of recovery copies.

The central rule is: having an old copy of a file does not give undo permission to overwrite its current contents.

Possibly related: [#15391 (https://github.com/anomalyco/opencode/issues/15391)](#15391) and [#11802 (https://github.com/anomalyco/opencode/issues/11802)](#11802).

Plugins

none

OpenCode version

1.18.34

Steps to reproduce

  1. User unwilling to reproduce steps here as he is seeking therapy and legal recourse.

Screenshot and/or share link

No response

Operating System

Darwin napkin 25.6.0 Darwin Kernel Version 25.6.0: Fri Jul 31 19:17:12 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T8103 arm64

Terminal

Terinal

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • #49732: Snapshot restore/revert bulk-overwrites the worktree from a stale baseline.
    • #44724: Snapshot restore force-rewrites every worktree file, including unchanged files.
    • #39513: Snapshot restore from a subdirectory overwrites unrelated sibling files.
    • #15391: /redo rewrites all files unnecessarily and alters modification times.
  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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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