decolua/9router · error

Unknown provider: ${providerInput}

Error message

Unknown provider: ${providerInput}

What it means

handleSingleProviderSearch normalizes the caller-supplied provider string via resolveProviderId and looks it up in the AI_PROVIDERS registry. If the resolved id has no registry entry, the provider is unknown and a 400 'Unknown provider: <input>' is returned. The registry is config-driven (providers/registry/*), so only registered search-capable provider ids resolve.

Source

Thrown at src/sse/handlers/search.js:99

      models: comboModels,
      handleSingleModel: (b, m) => handleSingleProviderSearch(b, m, request, apiKey, settings),
      log,
      comboName: providerInput,
      comboStrategy,
      comboStickyLimit
    });
  }

  return handleSingleProviderSearch(body, providerInput, request, apiKey, settings);
}

async function handleSingleProviderSearch(body, providerInput, request, apiKey, settings) {
  const query = body.query;
  const providerId = resolveProviderId(providerInput);
  const resolvedProvider = AI_PROVIDERS[providerId];

  if (!resolvedProvider) {
    log.warn("SEARCH", "Unknown provider", { provider: providerInput });
    return errorResponse(HTTP_STATUS.BAD_REQUEST, `Unknown provider: ${providerInput}`);
  }

  const providerConfig = resolvedProvider.searchConfig;
  const supportsSearch = !!providerConfig || !!resolvedProvider.searchViaChat;

  if (!supportsSearch) {
    log.warn("SEARCH", "Provider does not support web search", { provider: providerId });
    return errorResponse(HTTP_STATUS.BAD_REQUEST, `Provider ${providerId} does not support web search`);
  }

  if (providerInput !== providerId) {
    log.info("ROUTING", `${providerInput} → ${providerId}`);
  } else {
    log.info("ROUTING", `Provider: ${providerId}`);
  }

  // Sanitized body forwarded to core

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Use an exact registered provider id from AI_PROVIDERS (open-sse/providers/registry/) in the `provider`/`model` field.
  2. If you meant a combo, verify the combo name matches an entry in getCombos() and that its models array is non-empty.
  3. Check for typos and case; run resolveProviderId semantics (e.g. lowercase) against the input.
  4. After adding a new registry file, regenerate providers/registry/index.js with scripts/migrate-registry.mjs — hand-edits leave the provider unregistered.
  5. Update the client after upgrading 9Router, since provider ids can change between versions.

Example fix

// before
const body = { provider: 'Tavily Search', query: q }; // display name
// after
const body = { provider: 'tavily', query: q }; // registered provider id
Defensive patterns

Strategy: validation

Validate before calling

import { AI_PROVIDERS, resolveProviderId } from '@/shared/constants/providers.js';
if (!AI_PROVIDERS[resolveProviderId(provider)]) {
  throw new Error(`Unknown provider: ${provider}. Valid: ${Object.keys(AI_PROVIDERS).join(', ')}`);
}

Type guard

const isKnownProvider = (p) => Boolean(AI_PROVIDERS[resolveProviderId(String(p))]);

Prevention

When it happens

Trigger: Posting search with provider/model set to a typo ('tavilyy'), a display name instead of an id ('Google Search'), a combo name that fails getComboModelsFromData (name mismatch or combo has no models) and then fails provider resolution, or a provider removed/renamed in a newer version of the registry.

Common situations: Version mismatch between client config and server provider registry; copy-pasting model strings (containing '/') that are not combos; using a chat-only provider id with a '-search' suffix appended wrongly; stale cached dashboard state referencing a deleted provider.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/c04dbdf7035c3697. Report an issue: GitHub.