{"record":{"id":"42e6c00955c05f3b","repo":"vercel/ai","slug":"open-responses-extension-extension-id-cannot-pr","errorCode":null,"errorMessage":"Open Responses extension ${extension.id} cannot provide encodeToolChoice without toolType and encodeTool.","messagePattern":"Open Responses extension (.+?) cannot provide encodeToolChoice without toolType and encodeTool\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/open-responses/src/open-responses-extension.ts","lineNumber":185,"sourceCode":"    const namespace = extension.id.slice(0, namespaceSeparatorIndex);\n\n    registerUnique({\n      map: registry.byExtensionId,\n      key: extension.id,\n      extension,\n      field: 'id',\n    });\n\n    const hasToolType = extension.toolType != null;\n    const hasToolEncoder = extension.encodeTool != null;\n    if (hasToolType !== hasToolEncoder) {\n      throw new Error(\n        `Open Responses extension ${extension.id} must provide toolType and encodeTool together.`,\n      );\n    }\n\n    if (extension.encodeToolChoice != null && !hasToolEncoder) {\n      throw new Error(\n        `Open Responses extension ${extension.id} cannot provide encodeToolChoice without toolType and encodeTool.`,\n      );\n    }\n\n    if (hasToolType && hasToolEncoder) {\n      const toolExtension = extension as OpenResponsesToolExtension;\n      assertNamespacedType({\n        extensionId: extension.id,\n        namespace,\n        type: toolExtension.toolType,\n        field: 'toolType',\n      });\n      registerUnique({\n        map: registry.byProviderToolId,\n        key: extension.id,\n        extension: toolExtension,\n        field: 'provider-tool id',\n      });","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/open-responses/src/open-responses-extension.ts#L167-L203","documentation":"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.","triggerScenarios":"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).","commonSituations":"Copying a full tool extension and trimming encodeTool/toolType but keeping encodeToolChoice; building a choice-only extension assuming it can stand alone.","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."],"exampleFix":"// before\n{ id: 'acme.chart', encodeToolChoice: (c) => ({...}) }\n// after\n{ id: 'acme.chart', toolType: 'chart', encodeTool: encodeChartTool, encodeToolChoice: (c) => ({...}) }","handlingStrategy":"validation","validationCode":"extensions.forEach(e => {\n  const x = e as any;\n  if (x.encodeToolChoice != null && (x.toolType == null || x.encodeTool == null)) {\n    throw new Error(`${x.id}: encodeToolChoice requires toolType and encodeTool`);\n  }\n});","typeGuard":"function canEncodeToolChoice(e: object): boolean {\n  const x = e as any;\n  return x.encodeToolChoice == null || (x.toolType != null && x.encodeTool != null);\n}","tryCatchPattern":"try {\n  const registry = createOpenResponsesExtensionRegistry(extensions);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('cannot provide encodeToolChoice without toolType and encodeTool')) {\n    // add the missing tool encoder pair or drop encodeToolChoice\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["open-responses","extension","tool-choice","validation"],"backgroundTag":"invalid-extension-configuration","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}