vercel/ai · error

Open Responses extension ${extension.id} must provide toolTy

Error message

Open Responses extension ${extension.id} must provide toolType and encodeTool together.

What it means

An Open Responses tool extension must be internally consistent: toolType (the marker for decoding) and encodeTool (the encoder) are a matched pair. Providing exactly one of them would leave either encoding or decoding broken, so the registry throws during construction.

Source

Thrown at packages/open-responses/src/open-responses-extension.ts:179

    const namespaceSeparatorIndex = extension.id.indexOf('.');
    if (namespaceSeparatorIndex <= 0) {
      throw new Error(
        `Open Responses extension ID ${extension.id} must use <implementor>.<extension> format.`,
      );
    }
    const namespace = extension.id.slice(0, namespaceSeparatorIndex);

    registerUnique({
      map: registry.byExtensionId,
      key: extension.id,
      extension,
      field: 'id',
    });

    const hasToolType = extension.toolType != null;
    const hasToolEncoder = extension.encodeTool != null;
    if (hasToolType !== hasToolEncoder) {
      throw new Error(
        `Open Responses extension ${extension.id} must provide toolType and encodeTool together.`,
      );
    }

    if (extension.encodeToolChoice != null && !hasToolEncoder) {
      throw new Error(
        `Open Responses extension ${extension.id} cannot provide encodeToolChoice without toolType and encodeTool.`,
      );
    }

    if (hasToolType && hasToolEncoder) {
      const toolExtension = extension as OpenResponsesToolExtension;
      assertNamespacedType({
        extensionId: extension.id,
        namespace,
        type: toolExtension.toolType,
        field: 'toolType',
      });

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Add the missing counterpart: define toolType and encodeTool together.
  2. Remove both properties if the extension is not supposed to handle tools.
  3. Base new extensions on the OpenResponsesToolExtension type so TypeScript enforces the pair.

Example fix

// before
{ id: 'acme.chart', toolType: 'chart' } // encodeTool missing
// after
{ id: 'acme.chart', toolType: 'chart', encodeTool: (tool) => ({...}) }
Defensive patterns

Strategy: validation

Validate before calling

const hasToolType = (e: any) => e.toolType != null;
const hasEncodeTool = (e: any) => e.encodeTool != null;
extensions.forEach(e => { if (hasToolType(e) !== hasEncodeTool(e)) throw new Error(`${e.id}: toolType and encodeTool must be provided together`); });

Type guard

function isCompleteToolExtension(e: object): e is { toolType: unknown; encodeTool: Function } & Record<string, unknown> {
  const x = e as any;
  return (x.toolType != null) === (x.encodeTool != null);
}

Try / catch

try {
  const registry = createOpenResponsesExtensionRegistry(extensions);
} catch (e) {
  if (e instanceof Error && e.message.includes('must provide toolType and encodeTool together')) {
    // repair the extension definition
  }
  throw e;
}

Prevention

When it happens

Trigger: Registering an extension that defines toolType but no encodeTool, or encodeTool but no toolType, in the extensions array passed to the Open Responses model/registry.

Common situations: Implementing encodeTool and forgetting to declare toolType; deleting one property during refactoring; spreading/partially copying an existing extension object.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/32a786db5bf771cf. Report an issue: GitHub.