{"record":{"id":"203f96a516ac902f","repo":"can1357/oh-my-pi","slug":"cannot-register-custom-api-api-built-in-api","errorCode":null,"errorMessage":"Cannot register custom API \"${api}\": built-in API names are reserved.","messagePattern":"Cannot register custom API \"(.+?)\": built-in API names are reserved\\.","errorType":"exception","errorClass":"AIError.ConfigurationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/api-registry.ts","lineNumber":65,"sourceCode":"\toptions?: StreamOptions,\n) => AssistantMessageEventStream;\nexport type CustomStreamSimpleFn = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n) => AssistantMessageEventStream;\n\nexport interface RegisteredCustomApi {\n\tstream: CustomStreamFn;\n\tstreamSimple: CustomStreamSimpleFn;\n\tsourceId?: string;\n}\n\nconst customApiRegistry = new Map<string, RegisteredCustomApi>();\n\nfunction assertCustomApiName(api: string): void {\n\tif (BUILTIN_APIS.has(api as KnownApi)) {\n\t\tthrow new AIError.ConfigurationError(`Cannot register custom API \"${api}\": built-in API names are reserved.`);\n\t}\n}\n\n/**\n * Register a custom API streaming function.\n */\nexport function registerCustomApi(\n\tapi: string,\n\tstreamSimple: CustomStreamSimpleFn,\n\tsourceId?: string,\n\tstream?: CustomStreamFn,\n): void {\n\tassertCustomApiName(api);\n\tcustomApiRegistry.set(api, {\n\t\tstream: stream ?? ((model, context, options) => streamSimple(model, context, options as SimpleStreamOptions)),\n\t\tstreamSimple,\n\t\tsourceId,\n\t});","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/api-registry.ts#L47-L83","documentation":"registerCustomApi lets applications plug in custom streaming APIs, but built-in API names (the KnownApi set in BUILTIN_APIS) are reserved so routing and model dispatch stay unambiguous. assertCustomApiName checks the name against that set and throws ConfigurationError if it collides.","triggerScenarios":"Calling registerCustomApi with a name equal to a built-in API identifier (e.g. \"anthropic-messages\", \"openai-completions\", \"openai-responses\" or any other entry of BUILTIN_APIS).","commonSituations":"Trying to override/patch a built-in provider's behavior via the custom registry; copy-pasting a model's api field as the custom name; typos like \"openai-chat\" that collide with an actual built-in id.","solutions":["Choose a distinct namespaced custom API name, e.g. \"mycompany-anthropic-proxy\"","If the goal is to change built-in behavior, use the model's api field to point at your custom API name instead of re-registering the built-in name","Print or log BUILTIN_APIS / the KnownApi union to pick a non-colliding name"],"exampleFix":"// before\nregisterCustomApi(\"anthropic-messages\", myHandler) // reserved\n// after\nregisterCustomApi(\"acme-anthropic-messages\", myHandler)\n// then set model.api = \"acme-anthropic-messages\"","handlingStrategy":"validation","validationCode":"import { BUILTIN_APIS } from \"@oh-my-pi/pi-ai\"; // or the registry module exporting the set\nif (BUILTIN_APIS.has(myApiName)) {\n  throw new Error(`API name \"${myApiName}\" is reserved; pick a namespaced custom name`);\n}\nregisterCustomApi(myApiName, handler);","typeGuard":null,"tryCatchPattern":"try {\n  registerCustomApi(name, handler);\n} catch (err) {\n  if (err instanceof AIError.ConfigurationError && err.message.includes(\"reserved\")) {\n    registerCustomApi(`custom-${name}`, handler);\n  } else throw err;\n}","preventionTips":["Namespace custom API names with a company/project prefix","Never name custom APIs after built-in providers","Point models at custom behavior via the model's api field, not by overriding built-ins","Keep a central constant list of your app's custom API names"],"tags":["configuration","api-registry","naming"],"backgroundTag":"reserved-name-conflict","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}