deepseek-ai/deepseek-harness · error

: profile bundle declares no dsh.bundle in its package.json

Error message

${binName}: profile bundle ${JSON.stringify(packageName)} declares no dsh.bundle in its package.json

What it means

Thrown by loadProfile in dsh-app-boot when a package listed under dsh.profile.bundles in the profile's package.json resolves, but the package's own package.json has no dsh.bundle.patch declaration. Every profile layer must be a patch bundle, so a bundle-less package is treated as misconfiguration that fails loud at load instead of being read as 'no patches'. The message names the exact offending package.

Solutions

  1. Remove the offending package from dsh.profile.bundles in the profile's package.json — only patch bundles belong there
  2. If the package should act as a layer, add a dsh.bundle section with a patch path to its package.json and create that patch file
  3. Open the named package's package.json under node_modules and verify the dsh.bundle spelling and the patch path
  4. Run dsh plugin --profile <name> install so the profile's declared bundle dependencies are actually installed

Example fix

// before — profile package.json lists a plain package
"dsh": { "profile": { "bundles": ["@scope/some-utility"] } }
// @scope/some-utility has no dsh.bundle → loadProfile throws

// after — remove it, or make it a bundle:
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base"] } }
// in @scope/some-utility package.json (if it must patch the profile):
"dsh": { "bundle": { "patch": "cordis.patch.yml" } }
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs'

// Before booting the profile, verify each listed bundle declares a patch layer.
function assertProfileBundles(profileManifest, resolveDir) {
  for (const packageName of profileManifest.dsh?.profile?.bundles ?? []) {
    const pkg = JSON.parse(readFileSync(`${resolveDir(packageName)}/package.json`, 'utf8'))
    if (typeof pkg.dsh?.bundle?.patch !== 'string') {
      throw new Error(`${packageName} is not a patch bundle; remove it from dsh.profile.bundles`)
    }
  }
}

Type guard

function declaresPatch(pkg) {
  return typeof pkg?.dsh?.bundle?.patch === 'string' && pkg.dsh.bundle.patch.length > 0
}

Prevention

When it happens

Trigger: Running dsh --profile <name> (or any loadProfile caller) where the profile manifest's dsh.profile.bundles array contains a plain library package, a package whose dsh.bundle section lacks a patch field, or a package version that stopped shipping the manifest.

Common situations: Hand-editing a profile's package.json to add a convenience dependency; pointing bundles at a utility package that is not a dsh bundle; upgrading a bundle package that renamed or dropped its dsh.bundle block.

Related errors


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

Appendix: source

Thrown at packages/boot/app-boot/src/profile.ts:393

  const dir = resolveProfileDir(name, home)
  if (!existsSync(join(dir, 'package.json'))) {
    const template = PROFILE_TEMPLATES[name]
    if (template === undefined) {
      throw new Error(
        `${binName}: profile ${JSON.stringify(name)} does not exist; create it with 'dsh plugin --profile ${name} add <package>'`,
      )
    }
    initProfile(dir, template)
  }
  const manifest = normalizeShippedProfile(name, dir, readProfileManifest(binName, dir))
  // A hand-written profile manifest may omit the dsh section entirely.
  const bundles = manifest.dsh?.profile?.bundles ?? []
  const layers = bundles.map((packageName): ProfileLayer => {
    const packageDir = resolveBundleDir(binName, packageName, installAnchor, dir)
    const bundleManifest = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')) as ProfileManifest
    const declared = bundleManifest.dsh?.bundle?.patch
    if (declared === undefined) {
      throw new Error(`${binName}: profile bundle ${JSON.stringify(packageName)} declares no dsh.bundle in its package.json`)
    }
    const patchPath = join(packageDir, declared)
    return { packageName, packageDir, patchPath, patches: loadOverlayPatches(binName, patchPath) }
  })
  const patchPath = join(dir, PROFILE_PATCH_FILENAME)
  const patches = options.userLayer !== false && existsSync(patchPath)
    ? loadOverlayPatches(binName, patchPath)
    : []
  return { name, dir, layers, patchPath, patches }
}

/**
 * Compose patch layers into the effective entry list over an empty root —
 * the same single `applyEntryPatches` call the boot include makes, so flag
 * derivation and config dumps see exactly what mounts.
 * @param layers - patch lists in application order.
 * @param warn - sink for skipped-patch diagnostics; defaults to silent (boot repeats them).
 * @returns the composed entry list.

View on GitHub (pinned to b150a551b8)