{"record":{"id":"7b9597fe4f866d43","repo":"deepseek-ai/deepseek-harness","slug":"client-modules-boot-manifest-rev-must-be-a-string","errorCode":null,"errorMessage":"client-modules: boot manifest rev must be a string","messagePattern":"client-modules: boot manifest rev must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/client/modules/src/client/manifest.ts","lineNumber":153,"sourceCode":" */\nexport function stripClientSuffix(spec: string): string {\n  return spec.endsWith('/client') ? spec.slice(0, -'/client'.length) : spec\n}\n\n/**\n * Parse `window.__DSH_BOOT__` into the two consumer views. Wire boundary:\n * a missing or malformed graph throws (the shell shows the loud failure —\n * a page without a valid manifest cannot boot anything).\n * @param wire - the raw `window.__DSH_BOOT__` value.\n * @returns the manifest with optional plugin-view fields normalized.\n */\nexport function parseBootManifest(wire: unknown): BootManifest {\n  if (typeof wire !== 'object' || wire === null) {\n    throw new Error('client-modules: window.__DSH_BOOT__ is missing or not an object')\n  }\n  const graph = wire as Record<string, unknown>\n  if (typeof graph.rev !== 'string') {\n    throw new Error('client-modules: boot manifest rev must be a string')\n  }\n  if (!Array.isArray(graph.entries)) {\n    throw new Error('client-modules: boot manifest entries must be an array')\n  }\n  const modules: BootModuleRow[] = []\n  const plugins: BootPluginRow[] = []\n  for (const value of graph.entries as unknown[]) {\n    if (typeof value !== 'object' || value === null) {\n      throw new Error('client-modules: boot manifest entry is not an object')\n    }\n    const row = value as Record<string, unknown>\n    const where = typeof row.id === 'string' ? `\"${row.id}\"` : JSON.stringify(row)\n    if (typeof row.id !== 'string' || typeof row.url !== 'string' || typeof row.rev !== 'string') {\n      throw new Error(`client-modules: boot manifest entry ${where} must carry string id/url/rev`)\n    }\n    const subject = `boot manifest entry ${where}`\n    const inject = optionalStringArray(subject, 'inject', row.inject)\n    const external = optionalStringArray(subject, 'external', row.external)","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/client/modules/src/client/manifest.ts#L135-L171","documentation":"The boot graph's rev is the consistency anchor over the whole module graph (content and bundle hashes). parseBootManifest requires it to be a string; a numeric, null, or absent rev means the graph is malformed or was produced by an incompatible host, and the parse refuses it at the wire boundary before any module loads.","triggerScenarios":"window.__DSH_BOOT__ exists as an object but its rev is a number, null, or missing — a custom injection script, a stale host/frontend mix from different builds, or a hand-crafted test fixture.","commonSituations":"A test or custom host fabricating the graph with rev: Date.now(); mixing an old dsh binary with a newer frontend that changed the wire; manual edits of the injected global while debugging.","solutions":["Let the host generate the graph: restart dsh web (and pnpm --filter <pkg> bundle after client-plugin changes)","If you fabricate the wire yourself (fixtures, custom hosts), emit rev as a string","Use host and frontend from the same checkout/build so the wire shape matches"],"exampleFix":"// before (fixture/custom host)\nwindow.__DSH_BOOT__ = { rev: 1724500000000, entries: [...] }\n\n// after\nwindow.__DSH_BOOT__ = { rev: '1724500000000', entries: [...] }","handlingStrategy":"type-guard","validationCode":"const wire = (window as { __DSH_BOOT__?: unknown }).__DSH_BOOT__\nif (typeof wire === 'object' && wire !== null && typeof (wire as { rev?: unknown }).rev !== 'string') {\n  showFatalError('stale boot graph — rebuild client bundles and restart dsh web')\n}","typeGuard":"function hasStringRev(wire: object): wire is object & { rev: string } {\n  return typeof (wire as { rev?: unknown }).rev === 'string'\n}","tryCatchPattern":null,"preventionTips":["Never hand-write the boot graph; use the host's generator in tests","Rebuild and restart together when client plugins change","Treat the wire shape as version-pinned between host and frontend"],"tags":["manifest","validation","bootstrap","web"],"backgroundTag":"schema-validation-failed","analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}