{"record":{"id":"b8ef32abf7ba411f","repo":"can1357/oh-my-pi","slug":"provider-providername-must-specify-baseurl","errorCode":null,"errorMessage":"Provider ${providerName}: must specify \"baseUrl\", \"headers\", \"apiKey\", \"auth: none\", \"compat\", \"disableStrictTools\", \"guardrailIdentifier\", \"remoteCompaction\", \"modelOverrides\", \"discovery\", or \"models\"","messagePattern":"Provider (.+?): must specify \"baseUrl\", \"headers\", \"apiKey\", \"auth: none\", \"compat\", \"disableStrictTools\", \"guardrailIdentifier\", \"remoteCompaction\", \"modelOverrides\", \"discovery\", or \"models\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/models-config.ts","lineNumber":59,"sourceCode":"\tconst hasProviderApi = !!config.api;\n\tconst models = config.models;\n\n\tif (models.length === 0) {\n\t\tif (mode === \"models-config\") {\n\t\t\tconst hasModelOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;\n\t\t\tif (\n\t\t\t\t!config.baseUrl &&\n\t\t\t\t!config.headers &&\n\t\t\t\t!config.compat &&\n\t\t\t\t!config.apiKey &&\n\t\t\t\tconfig.auth !== \"none\" &&\n\t\t\t\t!config.disableStrictTools &&\n\t\t\t\t!config.guardrailIdentifier &&\n\t\t\t\t!config.remoteCompaction &&\n\t\t\t\t!hasModelOverrides &&\n\t\t\t\t!config.discovery\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Provider ${providerName}: must specify \"baseUrl\", \"headers\", \"apiKey\", \"auth: none\", \"compat\", \"disableStrictTools\", \"guardrailIdentifier\", \"remoteCompaction\", \"modelOverrides\", \"discovery\", or \"models\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\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);","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/models-config.ts#L41-L77","documentation":"validateProviderConfiguration rejects a provider entry that is effectively empty: in models-config mode with no `models` array, the config must contribute at least one meaningful setting (baseUrl, headers, apiKey, auth:none, compat, disableStrictTools, guardrailIdentifier, remoteCompaction, modelOverrides, or discovery). A provider stanza that sets none of these has nothing to apply and is rejected rather than silently ignored.","triggerScenarios":"Defining a provider in models.json / the models config file (via ModelsConfigFile, or indirectly through registerProvider) with only a name — e.g. `{ \"providers\": { \"mygw\": {} } }` or a stanza containing only unrelated keys like `name`/`description`.","commonSituations":"Hand-editing models.json and leaving a placeholder provider; deleting the baseUrl line while keeping the provider block; migrating configs where the old key names were dropped by a schema change, leaving an empty stanza.","solutions":["Add the intended setting — most commonly `baseUrl` plus `apiKey` — to the provider stanza.","If the provider was a leftover placeholder, delete the stanza entirely.","If you only wanted global tweaks, use the correct key (compat, modelOverrides, disableStrictTools, etc.) recognized by the schema."],"exampleFix":"// before (models.json)\n\"providers\": { \"mygw\": {} }\n// after\n\"providers\": { \"mygw\": { \"baseUrl\": \"https://gw.example.com/v1\", \"apiKey\": \"sk-...\", \"models\": [{ \"id\": \"mini\" }] } }","handlingStrategy":"validation","validationCode":"function providerStanzaMeaningful(cfg: Record<string, unknown>): boolean {\n  const keys = [\"baseUrl\", \"headers\", \"apiKey\", \"auth\", \"compat\", \"disableStrictTools\", \"guardrailIdentifier\", \"remoteCompaction\", \"modelOverrides\", \"discovery\", \"models\"];\n  return keys.some(k => cfg[k] !== undefined);\n}","typeGuard":"function isConfigurableProvider(cfg: object): cfg is { baseUrl: string } & Record<string, unknown> {\n  return \"baseUrl\" in cfg || \"models\" in cfg || \"discovery\" in cfg;\n}","tryCatchPattern":"try {\n  modelsConfig.apply(parsed);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('must specify \"baseUrl\"')) {\n    const provider = err.message.match(/Provider ([^:]+):/)?.[1];\n    logger.warn(`Provider ${provider} stanza is empty; removing or filling it required`);\n  }\n  throw err;\n}","preventionTips":["Delete placeholder provider stanzas instead of leaving them empty.","Validate models.json against the schema in CI to catch empty blocks before runtime.","After config migrations, diff provider blocks for keys dropped by renames.","Every provider entry should carry at least baseUrl or models."],"tags":["configuration","validation","models-config"],"backgroundTag":"missing-required-config-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}