Skip to content

Clearing time.archived silently fails, so archived sessions cannot be restored #47849

Closed

Description

@alohaninja

Description

Clearing time.archived on a session returns HTTP 200 and does nothing, so a session can be archived but never restored. Two independent defects sit on that path, and either one alone is enough to make it unreachable — which is why adding an "Unarchive" button doesn't fix it.

1. packages/core/src/session/projector.ts

time_archived: info.time.archived,

This passes undefined when the timestamp is being cleared. Drizzle omits undefined keys from .set(), so that means "leave this column alone", not "set NULL". The row keeps its previous value.

2. The session update handler (server/routes/instance/httpapi/handlers/session.ts)

if (ctx.payload.time?.archived !== undefined) {
  yield* session.setArchived(...)
}

A client clearing the field sends {"time":{}}, because JSON.stringify drops undefined-valued properties. So the guard short-circuits and setArchived is never called.

Steps to reproduce

Against a local opencode serve:

# archive
curl -X PATCH localhost:4096/session/$SID -H 'content-type: application/json' \
  -H "x-opencode-directory: $PWD" -d '{"time":{"archived":1700000000000}}'
# -> time.archived = 1700000000000

# attempt to clear
curl -X PATCH localhost:4096/session/$SID -H 'content-type: application/json' \
  -H "x-opencode-directory: $PWD" -d '{"time":{}}'
# -> 200 OK, but time.archived is still 1700000000000

Note ArchivedTimestamp is Schema.Finite and deliberately accepts 0 and negative values for legacy compatibility, so a truthiness check on archived is also incorrect — a session stored with 0 reads as active.

OpenCode version

dev @ 1b937c8 (also present in every version with the current projector)

Operating System

macOS 15 (server-side bug, not platform specific)

Activity

  1. alohaninja commented on Sep 17, 2026

    @alohaninja
    Author

    @nexxeln this fixes #47849, which is assigned to you. I reduced it to four backend/test files; the TUI and web changes are separate. The regression test archives, clears, and re-reads the session to check persistence. Could you take a look and approve the gated workflows, or point me to the right maintainer? Thanks!

  2. PaoloC68 commented on Sep 28, 2026

    @PaoloC68

    Data point that narrows this down: clearing is not universally broken — the failure depends on which falsy value is sent.

    On v1.18.31 I recovered an accidentally archived session by sending 0 rather than null/undefined:

    PATCH /session/{id}?directory=/path/to/project
    {"time":{"archived":0}}
    

    Before: time_archived = 1790608774698
    After: time_archived = 0

    The session reappeared in GET /session and in the web UI sidebar after a reload.

    That is consistent with the projector.ts diagnosis in the report rather than against it. 0 is a defined value, so Drizzle keeps the key in .set() and the column is written; undefined gets dropped and the row keeps its previous timestamp. So the bug is specifically that the cleared state is represented by a value Drizzle elides — not that the column is unwritable.

    Two practical consequences:

    1. There is a working user-level workaround today, which the issue currently says does not exist: PATCH with archived: 0. That matters because with no unarchive UI ([FEATURE]: Add unarchive/restore for archived sessions #24153, [FEATURE]:unarchive sessions #41403, feat(app): view archived sessions & unarchive #15250, [FEATURE]: view archived sessions on desktop #6680), this is the only way back and it is worth having findable while fix(session): clear time.archived so archived sessions can be restored #47848 is unmerged.
    2. If the fix lands as "write null", it is worth confirming readers treat 0 and null identically, since 0 rows now exist in the wild from anyone who used this workaround. In my case GET /session returned archived: 0 and both the list endpoint and the sidebar treated it as not archived, so the read path already appears falsy-based rather than null-based.

    Context on how the session got archived, which is why restore matters: session.archive is bound to mod+shift+backspace. Ctrl+Backspace is delete-previous-word, so holding Shift from a text selection archives the current session instead — silently, with no confirmation and no toast. Easy to trigger by accident, and at that point the session looks deleted.

  3. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    Thanks for the detailed report! In 2.1.0, sessions can be restored by updating them with archived: false; restoring now clears the saved archive timestamp rather than silently leaving it unchanged.

    Update with opencode upgrade or npm i -g @opencode/cli@latest (desktop: update the app). Please reopen or comment if restoration still fails on that version or newer.

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