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
On @deepseek-ai/dsh-desktop0.2.1-rc.3, moving a session between workspaces via the UI is not atomic: if the process is interrupted mid-move, it leaves two divergent copies of the same session id and makes workspaceRegistry fail on the next boot, which drags down 8 dependent services.
This is the producer side of the failure class reported in #1473 (that thread covers the consumer side with a skip-and-warn patch). #1473's open question explicitly asks: "The producer side is not yet diagnosed: we don't know which process rewrote/recompressed an artifact." The trigger below is cross-workspace session move.
Scenario: move a session with substantial history from workspace A → workspace B via the UI, then interrupt the process
Symptoms
UI error on move:
workspace create failed: gateway/service-unavailable: typert gateway: workspace/create:
active Service "workspaceController" is unavailable
Afterwards the workspace feature is dead: the dropdown stays on "loading workspaces…", the session list shows "no sessions" while 455 session files are on disk.
Root cause (startup log)
workspace (@deepseek-ai/dsh-workspace): Error: corrupt session log
"<DSH_HOME>/sessions/<workspace-B-key>/<session-id>/session.v4.jsonl.zstd":
header id "<session-id>" and cwd identify
"<DSH_HOME>/sessions/<workspace-A-key>/<session-id>/session.v4.jsonl.zstd"
at Proxy.assertStoredIdentity (.../dsh-session-persistence-jsonl/lib/index.js:3477:88)
The session log was placed in B's project dir, but header.cwd still says A. assertStoredIdentity derives the expected path from cwd + id and rejects the mismatch. This is exactly the assertStoredIdentity mismatch case that #1473's patch mentions it covers — so the consumer-side fix applies; what's missing is the producer-side guarantee.
Key evidence: two divergent copies
Same session id exists under both project dirs, with different content:
original (A)
target (B)
event lines
3045
3035
last seq
3043
3033
last write
T+12s
T
Line-by-line: the target copy is a strict prefix of the original (first 3035 lines byte-identical). So the move behaves as: copy a snapshot to target → keep appending 10 events to the original → process died. Not atomic.
storages/workspace.json was byte-identical to before the move (sha256 unchanged) — the registry was never updated either.
Blast radius
One corrupt artifact → workspaceRegistry fails → 8 services pending:
session-controller: pending (waiting for service: workspaceRegistry)
schedule: pending (waiting for service: sessionController)
workspace-controller: pending (waiting for service: workspaceRegistry)
ui-deliverables: pending (waiting for service: sessionController)
web-ui-task-board: pending (waiting for service: workspaceRegistry)
web-ui-git-graph: pending (waiting for service: workspaceRegistry)
web-ui-session-archive: pending (waiting for service: workspaceRegistry)
Suggestions
Make cross-workspace move a single recoverable transaction: update header.cwd → migrate the file → update the registry (and roll back on failure).
Extend workspacePendingMutation — it currently covers only workspace create/delete (discriminatedUnion has exactly two branches). Session moves have no recovery marker, so an interrupted move can only be repaired by hand.
Surface the root cause in the UI error, not only in the startup log.
Recovery note (non-destructive, for reference)
Full snapshot → validate all 455 session logs (zstd magic, per-line JSON validity, header.cwd + id ↔ actual path) → compare the two copies, keep the superset → move the redundant copy to a quarantine dir (not deleted) → full restart. Zero data loss, but entirely manual.
All identifiers redacted: session id → <session-id>, workspace paths → <workspace-A-key>/<workspace-B-key>, data dir → <DSH_HOME>. No conversation content or user identity included.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
On
@deepseek-ai/dsh-desktop0.2.1-rc.3, moving a session between workspaces via the UI is not atomic: if the process is interrupted mid-move, it leaves two divergent copies of the same session id and makesworkspaceRegistryfail on the next boot, which drags down 8 dependent services.This is the producer side of the failure class reported in #1473 (that thread covers the consumer side with a skip-and-warn patch). #1473's open question explicitly asks: "The producer side is not yet diagnosed: we don't know which process rewrote/recompressed an artifact." The trigger below is cross-workspace session move.
Environment
@deepseek-ai/dsh-desktop0.2.1-rc.3 (Electron 44.7.0 / Node 24.21.0)Symptoms
Root cause (startup log)
The session log was placed in B's project dir, but
header.cwdstill says A.assertStoredIdentityderives the expected path fromcwd + idand rejects the mismatch. This is exactly theassertStoredIdentitymismatch case that #1473's patch mentions it covers — so the consumer-side fix applies; what's missing is the producer-side guarantee.Key evidence: two divergent copies
Same session id exists under both project dirs, with different content:
Line-by-line: the target copy is a strict prefix of the original (first 3035 lines byte-identical). So the move behaves as: copy a snapshot to target → keep appending 10 events to the original → process died. Not atomic.
storages/workspace.jsonwas byte-identical to before the move (sha256 unchanged) — the registry was never updated either.Blast radius
One corrupt artifact →
workspaceRegistryfails → 8 services pending:Suggestions
header.cwd→ migrate the file → update the registry (and roll back on failure).workspacePendingMutation— it currently covers only workspacecreate/delete(discriminatedUnion has exactly two branches). Session moves have no recovery marker, so an interrupted move can only be repaired by hand.Recovery note (non-destructive, for reference)
Full snapshot → validate all 455 session logs (zstd magic, per-line JSON validity,
header.cwd+id↔ actual path) → compare the two copies, keep the superset → move the redundant copy to a quarantine dir (not deleted) → full restart. Zero data loss, but entirely manual.All identifiers redacted: session id →
<session-id>, workspace paths →<workspace-A-key>/<workspace-B-key>, data dir →<DSH_HOME>. No conversation content or user identity included.All reactions