can1357/oh-my-pi · error

Provider ${providerName}: "api" is required when registering

Error message

Provider ${providerName}: "api" is required when registering streamSimple.

What it means

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.

Source

Thrown at packages/coding-agent/src/config/model-registry.ts:2524

			if (activeSources.has(sourceId)) {
				continue;
			}
			this.clearSourceRegistrations(sourceId);
			this.#registeredProviderSources.delete(sourceId);
		}
	}

	/**
	 * Register a provider dynamically (from extensions).
	 *
	 * If provider has models: replaces all existing models for this provider.
	 * If provider has only baseUrl/headers: overrides existing models' URLs.
	 * If provider has streamSimple: registers a custom API streaming function.
	 * If provider has oauth: registers OAuth provider for /login support.
	 */
	registerProvider(providerName: string, config: ProviderConfigInput, sourceId?: string): void {
		if (config.streamSimple && !config.api) {
			throw new Error(`Provider ${providerName}: "api" is required when registering streamSimple.`);
		}

		validateProviderConfiguration(
			providerName,
			{
				baseUrl: config.baseUrl,
				headers: config.headers,
				apiKey: config.apiKey,
				api: config.api,
				oauthConfigured: Boolean(config.oauth),
				models: (config.models ?? []) as ProviderValidationModel[],
			},
			"runtime-register",
		);

		if (config.streamSimple && config.api) {
			const streamSimple = config.streamSimple;
			registerCustomApi(config.api, streamSimple, sourceId, (model, context, options) =>

View on GitHub (pinned to 9690622007)

Solutions

  1. Add the `api` field matching your streamSimple implementation's wire protocol to the ProviderConfigInput.
  2. If the stream function was accidental, remove `streamSimple` and rely on baseUrl/headers routing instead.
  3. Check the ProviderConfigInput type/docs for the accepted `api` values and pick the one your backend speaks.

Example fix

// before
registry.registerProvider("mygw", { streamSimple: myStream });
// after
registry.registerProvider("mygw", { api: "openai-completions", streamSimple: myStream });
Defensive patterns

Strategy: validation

Validate before calling

function assertStreamSimpleConfig(cfg: ProviderConfigInput): void {
  if (cfg.streamSimple && !cfg.api) {
    throw new Error('streamSimple requires "api"');
  }
}
assertStreamSimpleConfig(input); // call before registerProvider

Type guard

function hasApiForStreamSimple(cfg: ProviderConfigInput): cfg is ProviderConfigInput & { api: Api } {
  return !cfg.streamSimple || typeof cfg.api === "string";
}

Try / catch

try {
  registry.registerProvider(name, cfg);
} catch (err) {
  if (err instanceof Error && err.message.includes('"api" is required when registering streamSimple')) {
    registry.registerProvider(name, { ...cfg, api: "openai-completions" });
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/7999643ef6c23f77. Report an issue: GitHub.