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
- Provide toolType and encodeTool alongside encodeToolChoice.
- Remove encodeToolChoice if the extension does not manage tools.
- 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
- Only add encodeToolChoice after toolType and encodeTool are implemented.
- Use the typed OpenResponsesToolExtension interface for choice-bearing extensions.
- Keep tool-extension properties in one factory to guarantee coherence.
- Review extension diffs for partially-removed tool members.
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
- Open Responses extension ID ${extension.id} must use <implem
- Open Responses extension ${extension.id} must provide toolTy
- 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/42e6c00955c05f3b.
Report an issue: GitHub.