DeepSeek Harness workspaceController unavailable: restore
The failure most easily mistaken for "all my data is gone" is actually three different problems sharing one error: workspace create failed: gateway/service-unavailable: typert gateway: workspace/create: active Service "workspaceController" is unavailable. Reinstalling, rolling back, or creating a new profile when you see that line almost never helps, because the bad data is still in place. The right order is to start with the zero-cost, reversible action and only pay more as you go (#10885).
DeepSeek Harness: Why step one is "move it out", not "fix it back"
Moving a suspect directory out of the sessions root is reversible: nothing is deleted or edited, it just stops the startup scan from hitting it. One restart then tells you whether the data still exists, at the lowest possible cost.
- Quit DSH completely, including the tray icon and any leftover process in Task Manager.
- Copy the whole
~/.dsh/sessionstree to another drive as a cold backup. - Move every
*.archived-*, renamed directory, and directory of unknown origin outside the sessions root. - Start DSH and check whether the workspaces and sessions come back.
- If they do, the data is intact and only the scan stage was broken; if not, continue to priorities two and three.
The community result was hard evidence: after moving the renamed directory out, 23 workspaces and 867 sessions were rebuilt with zero loss, and all 824 v4 plus 43 older-format sessions decompressed normally.
DSH plugin: Priority 1: a session directory renamed in place (#10885)
When an external tool renames a session directory in place to archive it and then creates a same-named directory at the original path, one session-id lineage maps to two directories, and the startup scan throws and fails the whole list.
- Think about whether an external bridge manages sessions — a Feishu bot, for example. In this case the bridge lacked
resume_sessionin the SDK and rolled its own archive:feishu-<id>/→feishu-<id>.archived-<ts>/, then created a new<id>/. - Note the "incubation" period: the desktop app had not restarted for six days, and sessions are scanned only at startup, so the problem stayed hidden.
- The trigger was the rc.2→rc.3 nightly auto-update forcing a restart; the first full scan hit the renamed directory.
- The failure chain:
JsonlSessionPersistence.assertStoredIdentity()throws a plainError(corrupt session log ...: header id ... and cwd identify ...), whilelistArtifacts()filters onlySessionFormatUnsupportedErrorandSessionPersistenceCorruptionError. - The plain error escapes → the listing throws →
dsh-workspacenever activates →workspaceControlleris unavailable.
So this is not a version issue: the community compared the three related packages between rc.2 and rc.3, found identical MD5s, and reproduced the same error on rc.2's CLI against the same session root.
The community posted a 12-line patch on a fork (branch fix/list-survives-relocated-session-logs): during the list scan (expectedId === undefined), throw the identity mismatch as the typed SessionPersistenceCorruptionError so listArtifacts skips it through the existing corruption branch, while targeted loads still throw the plain error. It is not merged upstream.
DeepSeek Harness: Priority 2: workspace.json fails the schema (#10908)
workspace.json can be valid JSON and still fail: if any record in the workspaces array misses a field or has a non-ISO timestamp, the domain cannot open, workspaceRegistry never activates, and the shell only shows a generic service-unavailable.
- Quit the app completely and back up
~/.dsh/storages/workspace.json. - Open the file and check every record in
workspacesforpath,title,sessionIds,createdAt, andupdatedAt. - Fill timestamps in as ISO-8601 strings, for example
2026-01-01T00:00:00.000Z. The original error readscreatedAt: expected string, received undefined. - If a record is confirmed obsolete, delete it and remove the matching id from
global.workspaceIds. - Restart the app, start the host separately, and confirm stderr no longer shows
entries did not activateandworkspace/createreturnsok: true.
The confusing part is that a new profile, a version rollback, and a reinstall all fail to bypass it, because the bad record stays in the same storage file. It also has nothing to do with credentials, HOME, or directory permissions.
DSH Plugin: Priority 3: profile root unreachable, silent exit at startup (#10678)
When double-clicking the desktop app produces no window and the process exits with ENOENT ... mkdir ...\.dsh\profiles\desktop, that comes from the first line of withLock — mkdirSync — with no try block in front of it.
- Check whether
DSH_HOMEis a directory junction pointing elsewhere (for exampleG:\deepseek\.dshpointing at%USERPROFILE%\.dsh). - Check whether the junction target still exists: open the junction path in Explorer; a dangling junction lists no contents.
- Rename the earlier
.dsh.bakback to%USERPROFILE%\.dsh, or rebuild the junction to point at it. - Start again. If it recovers, this was the cause.
- If you never needed that junction layer, deleting it and restoring the backup in place is simpler — every extra junction is another failure point.
The mechanism is worth remembering: when a recursive: true mkdir crosses a dangling junction, it reports ENOENT on the leaf path, not "the junction is broken", so the log looks as if it is complaining that the profiles directory does not exist.
DeepSeek Harness: How to tell the three apart
Look at the symptom shape first: if the error is workspaceController unavailable but the window opens, check the first two; if no window appears at all, check the third.
| Symptom | Key evidence | Priority |
|---|---|---|
| Empty workspace list, creating fails | *.archived-* or renamed directories under the sessions root | Move them out (priority 1) |
| Same, with no renamed directory | Missing fields in ~/.dsh/storages/workspace.json | Fix schema (priority 2) |
| Double-click exits, no window | Log shows ENOENT mkdir profiles\desktop | Fix the junction (priority 3) |
DSH plugin: Confirming the recovery is real
Do not just look for a list in the window; confirm all three stages — scan, activate, create/delete — work.
- After restarting, count the workspaces and compare against the directory count in the backup.
- Open any old session and confirm the history renders and the conversation can continue.
- Create a new workspace and see whether
workspace create failedreturns. - Restart once more to confirm the state persists — if it only works inside the current process, another bad record is still there.
Cautions
Move and back up before deleting anything; the data is usually still there.
- Do not delete sessions first. Someone in the community suggested "delete everything and it will work", which throws away your ability to diagnose. Move first, back up first.
- Do not test with a new profile. The bad data lives in the same storage, and a new profile reads the same copy.
- Do not count on a version rollback. This chain is version-independent; the three related packages are byte-identical across both versions.
- Change the archiving tool's behavior. Any external tool that renames in place inside
sessions/recreates this state on every archive cycle. - Fully quit before restarting. Multiple instances scan at the same time, and when something breaks it becomes harder to tell which copy of the data is bad.
If all three steps are done and it is still unavailable, capture the full host stderr — the real cause is not in the generic UI message, only in that log.See DeepSeek Harness sessions won't open: null turn/end reason for the related case.
FAQ
Usually not lost: in the community case, moving a renamed session directory out of the sessions root let DeepSeek Harness rebuild all 23 workspaces and 867 sessions with zero loss; 824 v4 files and 43 older-format sessions all decompressed normally. Back up and move things out before judging.
DeepSeek Harness restores the workspaces because sessions are scanned only at startup, and assertStoredIdentity() throws a plain Error that listArtifacts() does not filter. The plain error escapes, the whole listing fails, dsh-workspace never activates, and workspaceController stays pending.
Valid JSON is not schema-valid in DeepSeek Harness: every record in the workspaces array needs path, title, sessionIds, createdAt, and updatedAt, and the timestamps must be ISO-8601 strings; one missing field throws a DomainError plus ZodError, the domain fails to open, and workspaceRegistry never activates.
No, none of those moves bypasses the workspaceController error in DeepSeek Harness, because the bad record is still in the same storage file and a new profile reads the same data. Rolling back does not help either — the community compared the three related packages between rc.2 and rc.3 and their MD5s were identical.
When DeepSeek Harness exits with no window and logs ENOENT mkdir .dsh\profiles\desktop, first check whether DSH_HOME is a directory junction whose target was renamed or moved. The first line of withLock is mkdirSync(profile, {recursive:true}) with no try block, so crossing a dangling junction reports ENOENT on the leaf path. Rename the backup back or rebuild the junction, then start again.
Related Terms
- workspaceController
- workspaceController is the DeepSeek Harness desktop service that adds, removes, and lists workspaces. It depends on workspaceRegistry activating; when any upstream step fails it stays pending and the UI only shows a generic active Service is unavailable.— https://github.com/deepseek-ai/deepseek-harness/discussions/10908
- assertStoredIdentity
- assertStoredIdentity is the JsonlSessionPersistence method that checks whether a session file's header id matches the path derived from cwd. After a directory is renamed in place it throws a plain Error, bypassing the type filter in listArtifacts.— https://github.com/deepseek-ai/deepseek-harness/discussions/10885
- workspaceRecord
- A workspaceRecord is the structure stored for a workspace and must contain path, title, sessionIds, createdAt, and updatedAt, with timestamps as ISO-8601 strings. Any record that fails this triggers a schema validation failure.— https://github.com/deepseek-ai/deepseek-harness/discussions/10908
- directory junction
- A directory junction is a Windows reparse point that points at another directory, often used to place DSH_HOME on a different drive. If the target is moved or renamed the junction dangles, and a recursive mkdir fails with ENOENT on the leaf path.— https://github.com/deepseek-ai/deepseek-harness/discussions/10678
Sources
- #10885 — after 0.2.1-rc.3, existing projects and sessions are gone and new ones cannot be created· deepseek-ai (GitHub Discussions)
- #10908 — Windows desktop cannot add a workspace: workspaceController is unavailable· deepseek-ai (GitHub Discussions)
- #10678 — desktop 0.2.1-rc.2: silent exit when the profile root is unreachable· deepseek-ai (GitHub Discussions)
- deepseek-ai/deepseek-harness (source repository)· GitHub