can1357/oh-my-pi · error
Provider ${providerName}, model ${modelDef.id}: no "api" spe
Error message
Provider ${providerName}, model ${modelDef.id}: no "api" specified. What it means
When registering per-model overlays in registerProvider, each custom model definition must resolve to an overlay carrying an `api` (either provider-level `config.api` or `modelDef.api`). If building the overlay returns null — meaning no API could be determined for that model — registerProvider throws this error naming the provider and model id.
Source
Thrown at packages/coding-agent/src/config/model-registry.ts:2607
if (config.models && config.models.length > 0) {
// Build model overlays that persist across refresh() cycles
const newOverlays: CustomModelOverlay[] = [];
for (const modelDef of config.models) {
const overlay = buildCustomModelOverlay(
providerName,
config.baseUrl!,
config.api,
config.headers,
config.apiKey,
config.authHeader,
config.compat,
undefined,
config.remoteCompaction,
modelDef as CustomModelDefinitionLike,
);
if (!overlay) {
throw new Error(`Provider ${providerName}, model ${modelDef.id}: no "api" specified.`);
}
newOverlays.push(overlay);
}
// Store as runtime overlays so they survive #reloadStaticModels()
this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(m => m.provider !== providerName);
this.#runtimeModelOverlays.push(...newOverlays);
// A modifier is explicitly a whole-catalog transform and may throw
// based on another provider's models. Preserve registration-time
// execution/error reporting by materializing only for this case.
if (config.oauth?.modifyModels && !this.#hasFullSnapshot) {
logger.time(`modelRegistry:materializeModifier:${providerName}`, () => this.#ensureFullSnapshot());
}
if (config.oauth?.modifyModels) {
this.#runtimeModelModifiers.set(providerName, config.oauth.modifyModels);
} else {
this.#runtimeModelModifiers.delete(providerName);
}View on GitHub (pinned to 9690622007)
Solutions
- Add `api` to the offending model definition in the `models` array.
- Alternatively set a provider-level `api` so all models inherit it.
- Verify the field name/casing is exactly `api` on the model definition object.
Example fix
// before
registerProvider("gw", { models: [{ id: "mini", baseUrl: "https://gw/v1" }] });
// after
registerProvider("gw", { models: [{ id: "mini", api: "openai-completions", baseUrl: "https://gw/v1" }] }); Defensive patterns
Strategy: validation
Validate before calling
for (const m of cfg.models ?? []) {
if (!cfg.api && !m.api) throw new Error(`model ${m.id}: set api at provider or model level`);
} Type guard
function everyModelHasApi(cfg: ProviderConfigInput): boolean {
return (cfg.models ?? []).every(m => Boolean(cfg.api || (m as { api?: unknown }).api));
} Try / catch
try {
registry.registerProvider(name, cfg);
} catch (err) {
if (err instanceof Error && err.message.includes('no "api" specified')) {
const id = err.message.match(/model ([^:]+):/)?.[1];
throw new Error(`Fix config: add "api" to model ${id} or set provider-level api`);
}
throw err;
} Prevention
- Prefer a provider-level `api` so every model inherits it; only override per model.
- Run the everyModelHasApi precheck on the models array before registration.
- Watch field casing: it is `api`, all lowercase, on the model definition.
- When moving model entries between providers, re-check that the destination provider declares api.
When it happens
Trigger: Calling registerProvider(name, { models: [{ id: "x", ... }] }) where neither the provider config nor the individual model definition specifies `api`. Triggered per offending modelDef inside the models array.
Common situations: Declaring custom models for a gateway whose dialect the registry cannot infer; copying a model entry that worked under a provider that had a provider-level api, into a provider without one; typos like `API` vs `api` or putting api inside the wrong nesting level of the model definition.
Related errors
- Provider ${providerName}: "api" is required when registering
- Provider ${providerName}: "api" is required when discovery i
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- ${name} path does not exist: ${trimmed}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1ac8d9196d740b42.
Report an issue: GitHub.