dsh-better-sidebar settings fail with "Failed to save HTTP 405" on DSH 0.2.1
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→405with an empty bodyGET /sidebar/file,GET /sidebar/bundle/*→404- DSH's own API is alive (
/api/sessions→401auth) - 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)
- Find the host port:
lsof -nP -iTCP -sTCP:LISTEN | grep DeepSeek(the desktop host is thedsh-desktop-hostprocess). - 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 403or a{ok:false,...}JSON envelope → plugin route is alive (fence rejecting you is correct behavior)
- bare empty-body
- Check
~/.dsh/logs/forpending (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.