{"record":{"id":"32a786db5bf771cf","repo":"vercel/ai","slug":"open-responses-extension-extension-id-must-prov","errorCode":null,"errorMessage":"Open Responses extension ${extension.id} must provide toolType and encodeTool together.","messagePattern":"Open Responses extension (.+?) must provide toolType and encodeTool together\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/open-responses/src/open-responses-extension.ts","lineNumber":179,"sourceCode":"    const namespaceSeparatorIndex = extension.id.indexOf('.');\n    if (namespaceSeparatorIndex <= 0) {\n      throw new Error(\n        `Open Responses extension ID ${extension.id} must use <implementor>.<extension> format.`,\n      );\n    }\n    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      });","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/open-responses/src/open-responses-extension.ts#L161-L197","documentation":"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.","triggerScenarios":"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.","commonSituations":"Implementing encodeTool and forgetting to declare toolType; deleting one property during refactoring; spreading/partially copying an existing extension object.","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."],"exampleFix":"// before\n{ id: 'acme.chart', toolType: 'chart' } // encodeTool missing\n// after\n{ id: 'acme.chart', toolType: 'chart', encodeTool: (tool) => ({...}) }","handlingStrategy":"validation","validationCode":"const hasToolType = (e: any) => e.toolType != null;\nconst hasEncodeTool = (e: any) => e.encodeTool != null;\nextensions.forEach(e => { if (hasToolType(e) !== hasEncodeTool(e)) throw new Error(`${e.id}: toolType and encodeTool must be provided together`); });","typeGuard":"function isCompleteToolExtension(e: object): e is { toolType: unknown; encodeTool: Function } & Record<string, unknown> {\n  const x = e as any;\n  return (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('must provide toolType and encodeTool together')) {\n    // repair the extension definition\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["open-responses","extension","tool-encoding","validation"],"backgroundTag":"invalid-extension-configuration","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}