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