{"record":{"id":"9bedc6e6e5d453a3","repo":"deepseek-ai/deepseek-harness","slug":"lookup-unavailable","errorCode":"lookup-unavailable","errorMessage":"lookup provider ${JSON.stringify(key)} is unavailable","messagePattern":"lookup provider (.+?) is unavailable","errorType":"exception","errorClass":"TypertGatewayError","httpStatus":null,"severity":"error","filePath":"packages/api/gateway/src/index.ts","lineNumber":431,"sourceCode":"    // takes undefined; a present-but-undefined field is not JSON-safe input and\n    // still fails decode. Lookup ids are never omissible, so absence here only\n    // ever belongs to a json parameter.\n    if (!Object.hasOwn(args, parameter.wire)) return undefined\n    const value = decode(parameter.codec, args[parameter.wire], 'input-invalid', endpoint, parameter.wire)\n    if (parameter.source === 'json') return value\n    const key = parameter.lookup\n    /* v8 ignore next -- registry validation rejects strict descriptors without a key, and SRC derivation always supplies one. */\n    if (key === undefined) {\n      throw new TypertGatewayError(\n        'lookup-unavailable',\n        endpoint,\n        `lookup parameter ${JSON.stringify(parameter.name)} has no provider key`,\n        { field: parameter.wire },\n      )\n    }\n    const provider = this.ctx.typert.lookups.get(key)\n    if (provider === undefined) {\n      throw new TypertGatewayError(\n        'lookup-unavailable',\n        endpoint,\n        `lookup provider ${JSON.stringify(key)} is unavailable`,\n        { field: parameter.wire },\n      )\n    }\n    if (provider.wire !== parameter.wire\n      || (parameter.codec.mode === 'strict' && provider.wireTypeSymbol !== parameter.codec.typeSymbol)) {\n      throw new TypertGatewayError(\n        'provider-mismatch',\n        endpoint,\n        `lookup provider ${JSON.stringify(key)} does not match its strict definition`,\n        { field: parameter.wire },\n      )\n    }\n    let resolved: unknown\n    try {\n      resolved = await provider.resolve(value)","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/api/gateway/src/index.ts#L413-L449","documentation":"A parameter the descriptor marks as source 'lookup' names a registry key, and resolveParameter fetches it with typert.lookups.get(key). This error means no live provider is registered under that key, so the Gateway cannot turn the wire id into the Host object the method actually receives; the wire value alone is not a legal argument.","triggerScenarios":"Invoking an endpoint with a lookup parameter (a method parameter matched by a TypertLookupMap declaration) while the plugin calling typert.lookups.register(<key>, provider) is not loaded, or was disposed after the descriptor was derived.","commonSituations":"Host composition includes the Remote-exporting plugin but not the plugin owning the lookup (sessions, documents, terminals); a renamed merge-declared lookup key with stale descriptors referencing the old key; HMR disposing the provider plugin mid-session.","solutions":["Load the plugin that registers the lookup provider under the exact key shown in the message.","Check the key spelling against the merge-declared TypertLookupMap entry used by both the descriptor and register().","Regenerate descriptors after renaming a lookup key so both sides agree.","Restart the host if HMR disposed the provider."],"exampleFix":"# before — lookup-consuming remote mounted, provider plugin missing\nplugins:\n  - dsh-doc-remotes\n# after — the plugin owning the 'document' lookup registers its provider\nplugins:\n  - dsh-doc           # calls typert.lookups.register('document', provider)\n  - dsh-doc-remotes","handlingStrategy":"validation","validationCode":"import type { Context } from '@deepseek-ai/cordis'\nimport type { InvocationDescriptor } from '@deepseek-ai/dsh-typert-protocol'\n\n// Confirm every lookup key the endpoint needs has a live provider before invoking.\nfunction lookupsAvailable(ctx: Context, descriptor: InvocationDescriptor): boolean {\n  return descriptor.parameters.every(parameter =>\n    parameter.source !== 'lookup'\n    || (parameter.lookup !== undefined && ctx.typert.lookups.get(parameter.lookup) !== undefined))\n}","typeGuard":"import { TypertGatewayError } from '@deepseek-ai/dsh-api-gateway'\n\nfunction isLookupUnavailable(error: unknown): error is TypertGatewayError {\n  return error instanceof TypertGatewayError && error.code === 'lookup-unavailable'\n}","tryCatchPattern":"try {\n  await gateway.invoke(request)\n} catch (error) {\n  if (error instanceof TypertGatewayError && error.code === 'lookup-unavailable') {\n    // host composition gap: fail fast with the missing key, no retry\n    throw new Error(`lookup provider missing on host (field ${error.field})`)\n  }\n  throw error\n}","preventionTips":["Derive a composition's plugin list from the lookup keys its remotes consume and assert each key is present at boot.","Watch typert.lookups.subscribe for removals and stop dispatching affected endpoints while a provider is gone.","Keep merge-declared lookup keys in shared modules so a rename touches every registration in one commit."],"tags":["typert","gateway","lookup-provider","registration","plugin-composition"],"backgroundTag":"service-provider-not-registered","analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}