{"record":{"id":"eb2c3f38c91f7924","repo":"can1357/oh-my-pi","slug":"provider-providername-api-is-required-when-d","errorCode":null,"errorMessage":"Provider ${providerName}: \"api\" is required when discovery is enabled at provider level.","messagePattern":"Provider (.+?): \"api\" is required when discovery is enabled at provider level\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/models-config.ts","lineNumber":82,"sourceCode":"\t} else {\n\t\tif (!config.baseUrl) {\n\t\t\tthrow new Error(`Provider ${providerName}: \"baseUrl\" is required when defining custom models.`);\n\t\t}\n\t\tconst requiresAuth =\n\t\t\tmode === \"runtime-register\"\n\t\t\t\t? !config.apiKey && !config.oauthConfigured\n\t\t\t\t: !config.apiKey && (config.auth ?? \"apiKey\") !== \"none\" && (config.auth ?? \"apiKey\") !== \"oauth\";\n\t\tif (requiresAuth) {\n\t\t\tthrow new Error(\n\t\t\t\tmode === \"runtime-register\"\n\t\t\t\t\t? `Provider ${providerName}: \"apiKey\" or \"oauth\" is required when defining models.`\n\t\t\t\t\t: `Provider ${providerName}: \"apiKey\" is required when defining custom models unless auth is \"none\" or \"oauth\".`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (mode === \"models-config\" && config.discovery && !config.api && config.discovery.type !== \"proxy\") {\n\t\tthrow new Error(`Provider ${providerName}: \"api\" is required when discovery is enabled at provider level.`);\n\t}\n\n\tfor (const modelDef of models) {\n\t\tif (!hasProviderApi && !modelDef.api) {\n\t\t\tthrow new Error(\n\t\t\t\tmode === \"runtime-register\"\n\t\t\t\t\t? `Provider ${providerName}, model ${modelDef.id}: no \"api\" specified.`\n\t\t\t\t\t: `Provider ${providerName}, model ${modelDef.id}: no \"api\" specified. Set at provider or model level.`,\n\t\t\t);\n\t\t}\n\t\tif (!modelDef.id) {\n\t\t\tthrow new Error(`Provider ${providerName}: model missing \"id\"`);\n\t\t}\n\t\tif (mode === \"models-config\") {\n\t\t\tif (modelDef.contextWindow !== undefined && modelDef.contextWindow <= 0) {\n\t\t\t\tthrow new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);\n\t\t\t}\n\t\t\tif (modelDef.maxTokens !== undefined && modelDef.maxTokens <= 0) {","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/models-config.ts#L64-L100","documentation":"Provider-level discovery (discovery.type other than \"proxy\") needs to know which wire API the discovered models speak, since the /models listing does not encode it. In models-config mode, validateProviderConfiguration throws when `discovery` is enabled at the provider level but neither `config.api` is set nor discovery.type is \"proxy\" (proxy discovery infers per-model APIs from supported_endpoint_types).","triggerScenarios":"A models-config provider with `{ discovery: { type: \"ollama\" | \"llama-cpp\" | \"models-list\" | ... } }` but no provider-level `api` field. Only discovery.type \"proxy\" is exempt.","commonSituations":"Configuring discovery for a local Ollama or LM Studio server and omitting `api`; switching from a proxy setup (where api is inferred) to a plain models-list discovery; copying the discovery block into a new provider without copying `api`.","solutions":["Add `api` to the provider config matching the backend dialect (e.g. \"openai-completions\" for Ollama/LM Studio /v1).","If the backend is a new-api/one-api style proxy advertising supported_endpoint_types, set discovery.type to \"proxy\" so APIs are inferred per model.","Move discovery from provider level into a form that specifies api per discovered group if you need mixed dialects."],"exampleFix":"// before\n{ \"provider\": \"lmstudio\", \"baseUrl\": \"http://localhost:1234/v1\", \"auth\": \"none\", \"discovery\": { \"type\": \"lm-studio\" } }\n// after\n{ \"provider\": \"lmstudio\", \"baseUrl\": \"http://localhost:1234/v1\", \"auth\": \"none\", \"api\": \"openai-completions\", \"discovery\": { \"type\": \"lm-studio\" } }","handlingStrategy":"validation","validationCode":"if (cfg.discovery && cfg.discovery.type !== \"proxy\" && !cfg.api) {\n  throw new Error(`Provider ${cfg.provider}: provider-level discovery needs \"api\"`);\n}","typeGuard":"function discoveryHasApi(cfg: { api?: unknown; discovery?: { type?: string } | null }): boolean {\n  return !cfg.discovery || cfg.discovery.type === \"proxy\" || typeof cfg.api === \"string\";\n}","tryCatchPattern":"try {\n  modelsConfig.apply(parsed);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('\"api\" is required when discovery is enabled')) {\n    const provider = err.message.match(/Provider ([^:]+):/)?.[1];\n    throw new Error(`Add \"api\" (or use discovery.type \"proxy\") for provider \"${provider}\"`);\n  }\n  throw err;\n}","preventionTips":["Set api alongside any provider-level discovery block.","Reserve discovery.type \"proxy\" for new-api/one-api upstreams that advertise supported_endpoint_types.","When copying discovery config between providers, copy api too.","Pre-validate with discoveryHasApi before writing models.json."],"tags":["configuration","validation","model-discovery","api"],"backgroundTag":"missing-required-config-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}