vercel/ai · error

Open Responses extension ${extension.id} cannot provide enco

Error message

Open Responses extension ${extension.id} cannot provide encodeToolChoice without toolType and encodeTool.

What it means

encodeToolChoice lets an extension encode tool-choice values for its tool type, but it depends on the tool encoder existing. The registry rejects an extension that sets encodeToolChoice without also providing toolType and encodeTool, since the encoded form of the choice cannot be interpreted without the tool machinery.

Source

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

    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',
      });
      registerUnique({
        map: registry.byProviderToolId,
        key: extension.id,
        extension: toolExtension,
        field: 'provider-tool id',
      });

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Provide toolType and encodeTool alongside encodeToolChoice.
  2. Remove encodeToolChoice if the extension does not manage tools.
  3. Type the extension as OpenResponsesToolExtension to get compile-time enforcement.

Example fix

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

Strategy: validation

Validate before calling

extensions.forEach(e => {
  const x = e as any;
  if (x.encodeToolChoice != null && (x.toolType == null || x.encodeTool == null)) {
    throw new Error(`${x.id}: encodeToolChoice requires toolType and encodeTool`);
  }
});

Type guard

function canEncodeToolChoice(e: object): boolean {
  const x = e as any;
  return x.encodeToolChoice == null || (x.toolType != null && x.encodeTool != null);
}

Try / catch

try {
  const registry = createOpenResponsesExtensionRegistry(extensions);
} catch (e) {
  if (e instanceof Error && e.message.includes('cannot provide encodeToolChoice without toolType and encodeTool')) {
    // add the missing tool encoder pair or drop encodeToolChoice
  }
  throw e;
}

Prevention

When it happens

Trigger: Registering an extension with encodeToolChoice defined but toolType/encodeTool absent (which also implies the previous pair check did not fire in this direction — here encodeTool is missing while encodeToolChoice is present).

Common situations: Copying a full tool extension and trimming encodeTool/toolType but keeping encodeToolChoice; building a choice-only extension assuming it can stand alone.

Related errors


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