DSH plugin startup: HTML did not preload client.js
If your page only shows HARNESS / Failed to load plugins and the console reports client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js, do not rush to reinstall dependencies. That sentence says a precise browser boot invariant failed: the page's window.__ModuleLoader__ facade has already run and called create(), but the registration queue has no factory for @deepseek-ai/dsh-client-modules. The problem is "the queue is missing that factory", not necessarily "the package is missing from disk" (#4836).
DSH plugin symptom: what Failed to load plugins and did not preload actually mean
The value of this error is how precise it is — it describes exactly one thing: the call happened before the factory was registered. Concretely:
- The UI and the stack are both short: the page shows
HARNESSandFailed to load plugins, and the console carries only this:
Error: client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js
at Object.create ((index):13:39)
at Cp.run (index.js:188:32)
Note (index):13:39 — the error occurs inside the inlined index script, not inside some bundle artifact, which by itself says the failure point is page assembly (#4836).
2. It is not a synonym for "package missing": the __ModuleLoader__ facade has run and create() has been called; its registration queue simply lacks the target factory. So "does the package exist on disk" and "is the factory registered in the queue" are two different checkpoints, and the error points only at the latter (#4836).
3. The failure surface is wider than it looks: incomplete or misordered HTML injection, a /plugins route returning something that is not JavaScript (404, a login page, or a proxy error page all count), a reverse proxy/CDN rewriting ?? or the rev query, CSP or TLS or a browser extension or a service worker blocking the script, async / defer / type=module being added to the script, or only the public entry failing while localhost works — all of these surface as the same error (#4836).
4. The easiest premise error: verifying in the wrong environment. Both the dynamic /plugins route and the HTML injection come from the Host plugin, and apps/web's own package contract states its dist is served by the CLI's dsh web. So a bare apps/web Vite dev server, an IDE preview, or a copied-out static dist/ does not carry this boot contract, and this error is unsurprising there (#4836).
5. The community frustration is a warning sign: one reporter gave up on the toolchain for the time being after hours of trying. That signals this error rarely converges by reinstalling; it has to be triaged by evidence (#4836).
DSH plugin mechanism: the browser boot invariant — queue, blocking bootstrap, boot graph
This invariant is precise because it has explicit steps and an order; understanding the order tells you where to collect evidence. Layer by layer:
- The correct alpha.1 startup order is five steps: ① inline
__ModuleLoader__queue; ② application preload links; ③ the parser-blocking/plugins/??...&rev=...bootstrap script; ④ inline__DSH_BOOT__graph; ⑤ the Web shell callscreate(). If any step is missing, wrong, or out of order, step ⑤ finds no factory in the queue (#4836). - Step ③ must be parser-blocking: once the script gets
async/defer/type=module,create()has a chance to run first — which is exactly the "facade ran while the queue was empty" shape. That makes this its own checkpoint (#4836). revis the anchor for version consistency: therevinjected into the HTML and the artifact actually returned by/plugins/??...&rev=...must come from the same build. So if a reverse proxy or CDN swallows or rewrites??(the multiplexed request) or the full query, the browser receives an artifact that does not match the HTML — while the error remains the same one (#4836).- Evidence must come from one page load: do not mix evidence from multiple restarts. This is precisely why many people "search for ages with no conclusion" — the two pieces come from different builds, so either both look fine or both look broken, and neither reflects the real failure (#4836).
- The error does not distinguish causes: interception (CSP / TLS / extension / service worker), a wrong route (404 or HTML returned), proxy rewriting (a lost query), assembly problems (missing or misordered), and environment mistakes (not a Host composition) all converge into the same sentence. It is an assertion, not a diagnosis — the diagnosis has to be completed with evidence (#4836).
DSH plugin triage and recovery: same-page-load evidence, then the checklist
Collecting evidence in order works far better than reinstalling repeatedly; fix one trustworthy piece of evidence first, then exclude items one by one. Specifically:
- Step one: capture the HTML and the bootstrap response from one page load. Save the HTML first, then copy the bootstrap URL verbatim out of it:
curl -fsS http://127.0.0.1:PORT/ -o dsh-index.html
grep -o '/plugins/[^"<]*' dsh-index.html
grep -n '__ModuleLoader__\|__DSH_BOOT__\|script src=' dsh-index.html
Then request that verbatim bootstrap URL with curl -i (#4836).
2. Step two: check these points one by one — ① does the HTML really contain the queue, the blocking bootstrap script, and the boot graph, in the right order; ② does /plugins return 200 JavaScript rather than a 404, a login page, or proxy error HTML; ③ does the proxy/CDN preserve ?? and the full query/rev; ④ is anything intercepted by CSP, TLS, a browser extension, or a service worker; ⑤ has the script been given async / defer / type=module; ⑥ does localhost work directly, i.e. is only the public proxy failing; ⑦ is the thing running the CLI's dsh web Host composition rather than a bare apps/web Vite server, an IDE preview, or a copied-out static dist/ (#4836).
3. Step three: to hand the problem to someone else, supply the checklist: launch command and cwd, version or commit, Node/pnpm versions, page URL, the bootstrap URL from the HTML, that URL's status and content-type, the failure reason from the browser's Network panel, and the Host activation / fiber logs. With those, the root cause can be narrowed to a single boundary (#4836).
4. Step four: the recovery path that recurs in the community is "Node version plus rebuild". Specifically: switching to Node 22 fixed it for one person; upgrading Node to v24.20.0 and rebuilding fixed it for another; a third resolved it by changing the code themselves. Because the original report did not confirm a single root cause, these are listed as verified recovery paths rather than the only explanation — and if your triage points at mismatched build artifacts, this path is especially worth trying first (#4836).
5. Step five: do not bypass the assertion. This check is protective: it would rather fail the plugin tree load than let the page keep running with an incomplete module set. Bypassing it only swaps "a clear failure" for "indeterminate behaviour". The community runbook likewise organises its safe recovery order around not bypassing the assertion (#4836).
6. What this means for plugin authors: this invariant shows that the front-end plugin loading contract depends on assembly order and version consistency. When you distribute a plugin with front-end build artifacts through DSH Plugin Hub, make sure your artifacts match the rev the host injects, and do not apply speed-up changes such as async / defer to host scripts — that breaks the contract directly (#4836).
DSH plugin troubleshooting notes
Remember first that this error is an assertion, not a diagnosis — it only says the call happened before the factory registered, and why it happened must be answered by evidence from one page load. Eight points to keep in mind when a DeepSeek Harness plugin fails to boot:
- Do not reinstall first: the error says the queue lacks a factory, not necessarily that a package is missing.
- Collect evidence within one page load: mixing evidence across restarts leads to wrong conclusions.
revmust match: the HTML and the/pluginsartifact have to come from the same build.- Confirm it is a Host composition: bare
apps/webVite, an IDE preview, and a staticdist/do not carry this contract. - Check whether the proxy swallows
??: the multiplexed request's query must not be dropped. - The script must be parser-blocking:
async/defer/type=modulebreak the order. - Try Node version plus rebuild first: it is the repeatedly verified recovery path.
- Do not bypass the assertion: it protects the premise that the page only continues with a complete module set.

Sources: Discussion #4836, Discussion #4885, web-client-plugin-boot-failure runbook.
FAQ
In a DSH plugin this error usually does not mean a missing npm package; it means a **precise browser boot invariant** failed. The error states that the page's window.__ModuleLoader__ facade has already run and called create(), but the registration queue has **no** factory for @deepseek-ai/dsh-client-modules. So the problem is "the queue is missing that factory", not necessarily "the package is missing from disk" — which is why collecting evidence in order beats reinstalling (Source: Discussion #4836).
In a DeepSeek Harness plugin the invariant depends on **version consistency**, so the HTML and the bootstrap response must come from the same page load. The rev injected into the HTML and the artifact actually returned by /plugins/??...&rev=... must come from the same build. If you restart the service and then capture the HTML and the script separately, the two pieces may come from different builds — so you end up with either "everything looks fine" or "everything looks broken", neither of which reflects the real failure. The method is one curl to save the HTML, copy the bootstrap URL verbatim out of it, and immediately curl -i that URL (Source: Discussion #4836).
In a DSH plugin both the dynamic /plugins route and the HTML injection come from the Host plugin, so verifying in the wrong environment reproduces the error. apps/web's own package contract states that its dist is served by the CLI's dsh web, not by starting its own server. So the CLI's dsh web Host composition has to be the thing running; a bare apps/web Vite server, an IDE preview, or a copied-out static dist/ cannot satisfy this boot contract, and hitting this error is then entirely expected (Source: Discussion #4836).
For a DeepSeek Harness plugin the usable paths reporters converged on all sit in the "Node version plus rebuild" group. One recovered after switching to Node 22; one upgraded Node to v24.20.0 and rebuilt, after which it worked; another resolved it by modifying the code themselves. Note these are **user-verified recovery paths**, and the original report did not confirm a single root cause — so this article is organised as "collect evidence first, then narrow the boundary by evidence" rather than asserting a cause up front (Source: Discussion #4836).
Related Terms
- preload invariant
- Before the Web shell calls create(), the page must have completed, in order: inline __ModuleLoader__ queue → application preload links → parser-blocking /plugins bootstrap script → inline __DSH_BOOT__ graph. Missing any step or breaking the order means create() finds no matching factory in the queue.— https://github.com/deepseek-ai/deepseek-harness/discussions/4836
- __ModuleLoader__ queue
- The inlined module-loader facade and its registration queue. It runs before all application scripts; when the facade has already called create() while the queue lacks the target factory, the did not preload error is thrown.— https://github.com/deepseek-ai/deepseek-harness/discussions/4836
- Host composition
- The host-side composition provided by the CLI's dsh web, which injects the HTML, serves the dynamic /plugins route, and mounts the Host plugin. It is not the same thing as a bare apps/web Vite dev server, which does not carry this boot contract.— https://github.com/deepseek-ai/deepseek-harness/discussions/4836
Sources
- deepseek-harness Discussion #4836: after startup, the page throws client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js· deepseek-ai (GitHub Discussions)
- Discussion #4885 (related report recovering after switching Node versions)· deepseek-ai (GitHub Discussions)
- Web client plugin boot failure runbook (evidence-first triage)· GitHub (sandbaseai)