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
- Add the missing counterpart: define toolType and encodeTool together.
- Remove both properties if the extension is not supposed to handle tools.
- 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
- Implement tool extensions from the OpenResponsesToolExtension interface so TS enforces the pair.
- Never delete one half of toolType/encodeTool during refactors — search for usages first.
- Write a helper factory that requires both fields as parameters.
- Add unit tests that construct the registry with each custom extension.
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
- Open Responses extension ID ${extension.id} must use <implem
- Open Responses extension ${extension.id} cannot provide enco
- Open Responses extension ${extension.id} must provide itemTy
- Open Responses extension ${extension.id} cannot provide enco
- maxEmbeddingsPerCall must be greater than 0
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/32a786db5bf771cf.
Report an issue: GitHub.