deepseek-ai/deepseek-harness · error · TypertGatewayError

lookup-unavailable

lookup-unavailable

Error message

lookup provider ${JSON.stringify(key)} is unavailable

What it means

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.

Source

Thrown at packages/api/gateway/src/index.ts:431

    // takes undefined; a present-but-undefined field is not JSON-safe input and
    // still fails decode. Lookup ids are never omissible, so absence here only
    // ever belongs to a json parameter.
    if (!Object.hasOwn(args, parameter.wire)) return undefined
    const value = decode(parameter.codec, args[parameter.wire], 'input-invalid', endpoint, parameter.wire)
    if (parameter.source === 'json') return value
    const key = parameter.lookup
    /* v8 ignore next -- registry validation rejects strict descriptors without a key, and SRC derivation always supplies one. */
    if (key === undefined) {
      throw new TypertGatewayError(
        'lookup-unavailable',
        endpoint,
        `lookup parameter ${JSON.stringify(parameter.name)} has no provider key`,
        { field: parameter.wire },
      )
    }
    const provider = this.ctx.typert.lookups.get(key)
    if (provider === undefined) {
      throw new TypertGatewayError(
        'lookup-unavailable',
        endpoint,
        `lookup provider ${JSON.stringify(key)} is unavailable`,
        { field: parameter.wire },
      )
    }
    if (provider.wire !== parameter.wire
      || (parameter.codec.mode === 'strict' && provider.wireTypeSymbol !== parameter.codec.typeSymbol)) {
      throw new TypertGatewayError(
        'provider-mismatch',
        endpoint,
        `lookup provider ${JSON.stringify(key)} does not match its strict definition`,
        { field: parameter.wire },
      )
    }
    let resolved: unknown
    try {
      resolved = await provider.resolve(value)

View on GitHub (pinned to b150a551b8)

Solutions

  1. Load the plugin that registers the lookup provider under the exact key shown in the message.
  2. Check the key spelling against the merge-declared TypertLookupMap entry used by both the descriptor and register().
  3. Regenerate descriptors after renaming a lookup key so both sides agree.
  4. Restart the host if HMR disposed the provider.

Example fix

# before — lookup-consuming remote mounted, provider plugin missing
plugins:
  - dsh-doc-remotes
# after — the plugin owning the 'document' lookup registers its provider
plugins:
  - dsh-doc           # calls typert.lookups.register('document', provider)
  - dsh-doc-remotes
Defensive patterns

Strategy: validation

Validate before calling

import type { Context } from '@deepseek-ai/cordis'
import type { InvocationDescriptor } from '@deepseek-ai/dsh-typert-protocol'

// Confirm every lookup key the endpoint needs has a live provider before invoking.
function lookupsAvailable(ctx: Context, descriptor: InvocationDescriptor): boolean {
  return descriptor.parameters.every(parameter =>
    parameter.source !== 'lookup'
    || (parameter.lookup !== undefined && ctx.typert.lookups.get(parameter.lookup) !== undefined))
}

Type guard

import { TypertGatewayError } from '@deepseek-ai/dsh-api-gateway'

function isLookupUnavailable(error: unknown): error is TypertGatewayError {
  return error instanceof TypertGatewayError && error.code === 'lookup-unavailable'
}

Try / catch

try {
  await gateway.invoke(request)
} catch (error) {
  if (error instanceof TypertGatewayError && error.code === 'lookup-unavailable') {
    // host composition gap: fail fast with the missing key, no retry
    throw new Error(`lookup provider missing on host (field ${error.field})`)
  }
  throw error
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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