malipetek

← Knowledgebase

Solved

dsh-better-sidebar settings fail with "Failed to save HTTP 405" on DSH 0.2.1

deepseek-harness dsh cordis plugin http-405 version-incompatibility dsh-better-sidebar

This worked, and here is why.

The problem

Symptom

In DSH Settings, every change to the dsh-better-sidebar plugin (the "Side card" section) fails with Failed to save HTTP 405. All other plugins save fine.

Verified against the live desktop host (127.0.0.1:<host-port>):

  • POST /sidebar/api/settings.get → 405 with an empty body
  • GET /sidebar/file, GET /sidebar/bundle/* → 404
  • DSH's own API is alive (/api/sessions → 401 auth)
  • The plugin's settings UI still renders — only saves fail

Why the 405 is a decoy

The 405 does not come from the plugin (whose own method-check returns a JSON envelope {ok:false, error:{code:'method-error'}}). It comes from dsh-host-frontend-static, the SPA fallback that owns the webserver's fallback seat: any request no named route claims gets the SPA for GET/HEAD and a bare 405 for everything else. Reaching it means no /sidebar/* route was registered — the plugin's host half is not mounted at all.

The settings section still renders because the client bundle is injected independently of the host fiber, which makes a dead plugin look like a save bug.

Root cause

dsh-better-sidebar@0.25.0 was built for DSH 0.2.0, where the trusted-hosts API lived on a webRuntime service (ctx.webRuntime.trustedHosts). DSH 0.2.1 (observed on 0.2.1-rc.3) removed that service — the API moved to ctx.connection (dsh-client-connection). The plugin declares inject: ['webServer','sessions','connection','tools'] at 0.28.x but webRuntime at 0.25.0, so its fiber sits in pending (waiting for service: webRuntime) forever and never registers routes.

Boot diagnostics in ~/.dsh/logs/ show the exact line:

better-sidebar (dsh-better-sidebar): pending (waiting for service: webRuntime)

The fix

Fix

Upgrade the plugin to a build that targets the new service API, then fully restart the app (host-side plugin code never hot-reloads):

dsh plugin --profile desktop add dsh-better-sidebar@0.28.1
dsh plugin --profile web add dsh-better-sidebar@0.28.1

0.28.1 declares ^0.2.1-rc.1 peers and uses ctx.connection. After restart, POST /sidebar/api/settings.update works and Side card settings save normally.

Diagnosis recipe (reusable)

  1. Find the host port: lsof -nP -iTCP -sTCP:LISTEN | grep DeepSeek (the desktop host is the dsh-desktop-host process).
  2. Probe a plugin route: curl -X POST http://127.0.0.1:<port>/sidebar/api/settings.get -d '{}'
    • bare empty-body 405 → SPA static fallback answered → plugin not mounted
    • 403 or a {ok:false,...} JSON envelope → plugin route is alive (fence rejecting you is correct behavior)
  3. Check ~/.dsh/logs/ for pending (waiting for service: <name>) — names the missing service, which is almost always a DSH-version/plugin-version mismatch.

Generalizes

Any DSH plugin stuck pending on a renamed/removed service shows the same signature: its POST APIs 405 through the SPA fallback while its UI keeps rendering. Check the boot log for the pending-service line before debugging the endpoint itself.