deepseek-ai/deepseek-harness · critical

client-modules: window.__DSH_BOOT__ is missing or not an obj

Error message

client-modules: window.__DSH_BOOT__ is missing or not an object

What it means

The dsh web host injects the composed module graph into the page as window.__DSH_BOOT__ before the client module system starts; parseBootManifest enforces that wire boundary and throws when the value is missing or not an object. The built apps/web Vite shell is not a standalone application — only dsh web performs the injection — so a page served any other way fails here, and the shell shows the loud failure because a page without a valid manifest cannot boot anything.

Source

Thrown at packages/client/modules/src/client/manifest.ts:149

 * require path and graph composition normalize here, which is what lets each
 * importing package request the subpath its own code imports.
 * @param spec - module specifier as a bundle requires it or a declaration spells it.
 * @returns the specifier with a trailing `/client` removed.
 */
export function stripClientSuffix(spec: string): string {
  return spec.endsWith('/client') ? spec.slice(0, -'/client'.length) : spec
}

/**
 * Parse `window.__DSH_BOOT__` into the two consumer views. Wire boundary:
 * a missing or malformed graph throws (the shell shows the loud failure —
 * a page without a valid manifest cannot boot anything).
 * @param wire - the raw `window.__DSH_BOOT__` value.
 * @returns the manifest with optional plugin-view fields normalized.
 */
export function parseBootManifest(wire: unknown): BootManifest {
  if (typeof wire !== 'object' || wire === null) {
    throw new Error('client-modules: window.__DSH_BOOT__ is missing or not an object')
  }
  const graph = wire as Record<string, unknown>
  if (typeof graph.rev !== 'string') {
    throw new Error('client-modules: boot manifest rev must be a string')
  }
  if (!Array.isArray(graph.entries)) {
    throw new Error('client-modules: boot manifest entries must be an array')
  }
  const modules: BootModuleRow[] = []
  const plugins: BootPluginRow[] = []
  for (const value of graph.entries as unknown[]) {
    if (typeof value !== 'object' || value === null) {
      throw new Error('client-modules: boot manifest entry is not an object')
    }
    const row = value as Record<string, unknown>
    const where = typeof row.id === 'string' ? `"${row.id}"` : JSON.stringify(row)
    if (typeof row.id !== 'string' || typeof row.url !== 'string' || typeof row.rev !== 'string') {
      throw new Error(`client-modules: boot manifest entry ${where} must carry string id/url/rev`)

View on GitHub (pinned to b150a551b8)

Solutions

  1. Open the GUI at the URL dsh web prints (loopback or declared authority) — the injection happens only there
  2. If developing the shell, keep dev rebuild tooling running but still load the page through the running dsh web server
  3. On a dsh-served URL that still fails, check for a blocked inline boot script (CSP, extensions)

Example fix

// before
const manifest = parseBootManifest((window as any).__DSH_BOOT__)

// after
const wire = (window as { __DSH_BOOT__?: unknown }).__DSH_BOOT__
if (typeof wire !== 'object' || wire === null) {
  throw new Error('serve this page via dsh web; window.__DSH_BOOT__ is host-injected')
}
const manifest = parseBootManifest(wire)
Defensive patterns

Strategy: type-guard

Validate before calling

const wire = (window as { __DSH_BOOT__?: unknown }).__DSH_BOOT__
if (typeof wire !== 'object' || wire === null) {
  showFatalError('open this page through the dsh web URL — the host injects window.__DSH_BOOT__')
}

Type guard

function hasBootManifest(w: Window & { __DSH_BOOT__?: unknown }): w is Window & { __DSH_BOOT__: object } {
  return typeof w.__DSH_BOOT__ === 'object' && w.__DSH_BOOT__ !== null
}

Prevention

When it happens

Trigger: Opening the built frontend index.html directly from disk, via vite dev / vite preview, or behind a plain static server; or a host-side inline injection script that failed (CSP or extension blocking) before assigning the global.

Common situations: Frontend development opening the Vite dev server instead of the dsh-served URL; deploying dist/ behind nginx standalone; strict CSP stripping the inline boot script.

Related errors


AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24). Data as JSON: /api/errors/e48aae67a000ab08. Report an issue: GitHub.