deepseek-ai/deepseek-harness · error · MissingClientBundleError

client-modules: client bundle not found; run `pnpm run build

Error message

client-modules: client bundle not found; run `pnpm run build` before launch:
  package: ${packageName}
  path: ${clientPath}

What it means

MissingClientBundleError from initialBundleRevision(): when the scan registers a client package, it hashes the built bundle file that exports["./client"] points at; an ENOENT on that read becomes this error (other filesystem errors rethrow unchanged), carrying the package name and the exact missing path. It is retained as structured data so the activation aggregate (index 144) can group every unbuilt package under one build instruction.

Source

Thrown at packages/client/modules/src/index.ts:477

      immediately: decl.immediately === true,
    }
    this.pkgMeta.set(pkgName, meta)
    return meta
  }

  /**
   * Read the activation-time bundle revision.
   * @param pkgName - package that declares the client bundle.
   * @param clientPath - absolute path of the built client artifact.
   * @returns the bundle content's short hash for use as its revision.
   * @throws {MissingClientBundleError} when the read fails with `ENOENT`; other filesystem errors are rethrown unchanged.
   */
  private initialBundleRevision(pkgName: string, clientPath: string): string {
    try {
      return shortHash(readFileSync(clientPath))
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
      throw new MissingClientBundleError(pkgName, clientPath, error)
    }
  }

  /** Reconcile one entry name against the live loader entries. @returns whether the table changed. */
  private processOne(entryName: string): boolean {
    let qualifies = false
    for (const entry of this.ctx.loader.entries()) {
      if (entry.options.name === entryName && entry.fiber !== undefined && !entry.disabled) {
        qualifies = true
        break
      }
    }
    if (!qualifies) return this.table.delete(entryName)
    if (this.table.has(entryName)) return false
    const meta = this.resolveMeta(entryName)
    if (meta === null) return false
    // The rev rides the row from here on: a fiber restart reuses the row (and
    // its rev) untouched; only rebuilt() re-reads the bundle.

View on GitHub (pinned to b150a551b8)

Solutions

  1. Build the named package's client bundle: `pnpm --filter <packageName> bundle` (or repo-wide `pnpm run build`), then restart — the message prints the exact path to verify.
  2. If the printed path is not the package's real build output, fix exports["./client"] in that package.json to point at the actual bundle.
  3. Put bundle builds into the dev/CI launch script so artifacts exist before the server starts.

Example fix

# before — probe a live server after building only the node half
pnpm --filter @deepseek-ai/dsh-client-ui-workspace build
dsh web   # client bundle not found; path: .../ui-workspace/lib/client.js

# after — build the bundle the registry serves
pnpm --filter @deepseek-ai/dsh-client-ui-workspace bundle
dsh web
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'node:fs'
// before launching the web server, verify each client package's bundle is on disk
const bundlesReady = (clientPaths: string[]) => clientPaths.every(existsSync)

Prevention

When it happens

Trigger: A dsh.client package is an active Loader entry with a correct manifest, but the file at its './client' export path (typically lib/client.js) does not exist: launched without building client bundles, `pnpm run clean` removed lib/, or a filtered build skipped that package.

Common situations: Editing a plugin's node half and probing a live `dsh web` server without running its `bundle` script — the registry serves lib/client.js, not sources; CI artifact cache missing lib/; clean-then-run workflows.

Related errors


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