{"record":{"id":"7999643ef6c23f77","repo":"can1357/oh-my-pi","slug":"provider-providername-api-is-required-when-r","errorCode":null,"errorMessage":"Provider ${providerName}: \"api\" is required when registering streamSimple.","messagePattern":"Provider (.+?): \"api\" is required when registering streamSimple\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/model-registry.ts","lineNumber":2524,"sourceCode":"\t\t\tif (activeSources.has(sourceId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.clearSourceRegistrations(sourceId);\n\t\t\tthis.#registeredProviderSources.delete(sourceId);\n\t\t}\n\t}\n\n\t/**\n\t * Register a provider dynamically (from extensions).\n\t *\n\t * If provider has models: replaces all existing models for this provider.\n\t * If provider has only baseUrl/headers: overrides existing models' URLs.\n\t * If provider has streamSimple: registers a custom API streaming function.\n\t * If provider has oauth: registers OAuth provider for /login support.\n\t */\n\tregisterProvider(providerName: string, config: ProviderConfigInput, sourceId?: string): void {\n\t\tif (config.streamSimple && !config.api) {\n\t\t\tthrow new Error(`Provider ${providerName}: \"api\" is required when registering streamSimple.`);\n\t\t}\n\n\t\tvalidateProviderConfiguration(\n\t\t\tproviderName,\n\t\t\t{\n\t\t\t\tbaseUrl: config.baseUrl,\n\t\t\t\theaders: config.headers,\n\t\t\t\tapiKey: config.apiKey,\n\t\t\t\tapi: config.api,\n\t\t\t\toauthConfigured: Boolean(config.oauth),\n\t\t\t\tmodels: (config.models ?? []) as ProviderValidationModel[],\n\t\t\t},\n\t\t\t\"runtime-register\",\n\t\t);\n\n\t\tif (config.streamSimple && config.api) {\n\t\t\tconst streamSimple = config.streamSimple;\n\t\t\tregisterCustomApi(config.api, streamSimple, sourceId, (model, context, options) =>","sourceCodeStart":2506,"sourceCodeEnd":2542,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/model-registry.ts#L2506-L2542","documentation":"ModelRegistry.registerProvider enforces that a provider config supplying `streamSimple` (a custom streaming function) also declares `api`, because the runtime needs to know which wire API the stream function implements. Without `api` the registry cannot type or route the custom streamer, so it throws immediately.","triggerScenarios":"Calling registerProvider(name, { streamSimple: fn, ... }) without `api` — e.g. an SDK user or plugin registers a custom streaming transport but forgets to specify which API dialect (\"openai-completions\", \"anthropic-messages\", etc.) it speaks.","commonSituations":"Writing a custom provider plugin that proxies a nonstandard backend; adapting an internal gateway; copying an older config example from before the api field was required for streamSimple.","solutions":["Add the `api` field matching your streamSimple implementation's wire protocol to the ProviderConfigInput.","If the stream function was accidental, remove `streamSimple` and rely on baseUrl/headers routing instead.","Check the ProviderConfigInput type/docs for the accepted `api` values and pick the one your backend speaks."],"exampleFix":"// before\nregistry.registerProvider(\"mygw\", { streamSimple: myStream });\n// after\nregistry.registerProvider(\"mygw\", { api: \"openai-completions\", streamSimple: myStream });","handlingStrategy":"validation","validationCode":"function assertStreamSimpleConfig(cfg: ProviderConfigInput): void {\n  if (cfg.streamSimple && !cfg.api) {\n    throw new Error('streamSimple requires \"api\"');\n  }\n}\nassertStreamSimpleConfig(input); // call before registerProvider","typeGuard":"function hasApiForStreamSimple(cfg: ProviderConfigInput): cfg is ProviderConfigInput & { api: Api } {\n  return !cfg.streamSimple || typeof cfg.api === \"string\";\n}","tryCatchPattern":"try {\n  registry.registerProvider(name, cfg);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('\"api\" is required when registering streamSimple')) {\n    registry.registerProvider(name, { ...cfg, api: \"openai-completions\" });\n  } else throw err;\n}","preventionTips":["Always pair streamSimple with an explicit api in the same config object.","Model the config with a discriminated type that makes api mandatory when streamSimple is present.","Validate provider input with assertStreamSimpleConfig before calling registerProvider.","Keep custom-streamer examples updated so copied snippets include api."],"tags":["configuration","validation","model-registry","api"],"backgroundTag":"missing-required-config-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}