deepseek-ai/deepseek-harness · error

client-modules: ${pkgName} declares dsh.client but exports n

Error message

client-modules: ${pkgName} declares dsh.client but exports no "./client" bundle

What it means

resolveMeta() accepts a package as a web client row only if its package.json both declares dsh.client with platform 'web' and exposes a './client' export (a string, or an object whose default is a string). If the declaration exists but exports["./client"] is absent or does not resolve to a string path, the scan throws this error; it surfaces inside the activation aggregate (index 144) or as a steady-state warning. The mismatch is a package-manifest defect: the manifest promises a browser half but never wired its entry.

Source

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

    } catch {
      // Not a resolvable package root: loader builtins (cordis:include) and
      // subpath entries (…/gateway) land here — permanently not a client row.
      this.pkgMeta.set(pkgName, null)
      return null
    }
    const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as Record<string, unknown>
    const dsh = pkg.dsh
    const decl = parseDshClient(
      pkgName,
      dsh !== null && typeof dsh === 'object' ? (dsh as Record<string, unknown>).client : undefined,
    )
    if (decl === undefined || decl.platform !== 'web') {
      this.pkgMeta.set(pkgName, null)
      return null
    }
    const clientRel = clientExportOf(pkgName, pkg.exports)
    if (clientRel === undefined) {
      throw new Error(`client-modules: ${pkgName} declares dsh.client but exports no "./client" bundle`)
    }
    const meta: PkgMeta = {
      clientPath: join(dirname(pkgPath), clientRel),
      ...(decl.inject !== undefined ? { inject: decl.inject } : {}),
      external: decl.external ?? [],
      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.
   */

View on GitHub (pinned to b150a551b8)

Solutions

  1. Add the missing export to that package's package.json: "./client": "./lib/client.js" (or { "default": "./lib/client.js" }).
  2. Build the target so the file exists: `pnpm --filter <pkg> bundle`.
  3. Run verify-client-packages to catch manifest drift, then restart the server — per-name package metadata is cached and plugin-set changes take effect on restart.

Example fix

// before — package.json
{ "exports": { ".": "./lib/index.js" }, "dsh": { "client": { "platform": "web" } } }

// after
{
  "exports": { ".": "./lib/index.js", "./client": "./lib/client.js" },
  "dsh": { "client": { "platform": "web" } }
}
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs'
function clientExportResolves(pkgDir) {
  const pkg = JSON.parse(readFileSync(`${pkgDir}/package.json`, 'utf8'))
  if (pkg.dsh?.client?.platform !== 'web') return true // not a client package
  const c = pkg.exports?.['./client']
  return typeof c === 'string' || typeof c?.default === 'string'
}

Prevention

When it happens

Trigger: A Loader entry's package.json contains dsh.client with platform 'web' while its exports map has no "./client" key, or the key is neither a string nor an object with a string default. Typical when the manifest was hand-edited or the new-plugin-package checklist's exports step was skipped.

Common situations: Bringing up a new packages/client/<name> plugin package from a skeleton and forgetting the './client' exports entry; renaming or restructuring exports during a refactor; a JSON merge that silently dropped the key.

Related errors


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