{"record":{"id":"d6f02429e9ad806e","repo":"can1357/oh-my-pi","slug":"unknown-search-provider-id","errorCode":null,"errorMessage":"Unknown search provider: ${id}","messagePattern":"Unknown search provider: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/search/provider.ts","lineNumber":185,"sourceCode":"}\n\n/** Format the ordered provider fallback failures for terminal/tool output. */\nexport function formatSearchProviderFailures(\n\tfailures: readonly { provider: Pick<SearchProvider, \"id\" | \"label\">; error: unknown }[],\n): string {\n\treturn failures.map(f => `${f.provider.id}: ${formatSearchProviderFailure(f.error, f.provider)}`).join(\"; \");\n}\n\n/**\n * Resolve and cache a provider instance. First call for a given id loads the\n * underlying module; subsequent calls return the cached singleton.\n */\nexport async function getSearchProvider(id: SearchProviderId): Promise<SearchProvider> {\n\tconst cached = instanceCache.get(id);\n\tif (cached) return cached;\n\tconst meta = PROVIDER_META[id];\n\tif (!meta) {\n\t\tthrow new Error(`Unknown search provider: ${id}`);\n\t}\n\tconst provider = await meta.load();\n\tinstanceCache.set(id, provider);\n\treturn provider;\n}\n\n/** Provider fallback order set via settings (default: built-in order). */\nlet orderedProvIds: readonly SearchProviderId[] = SEARCH_PROVIDER_ORDER;\n/** Providers the user explicitly listed in `providers.webSearchOrder`. */\nlet explicitProvIds = new Set<SearchProviderId>();\n\n/**\n * Prioritize configured providers while retaining every unlisted provider in\n * its built-in relative order. Invalid IDs are ignored defensively. Listed\n * providers are treated as explicit selections: they resolve through\n * `isExplicitlyAvailable`, so e.g. a hand-listed Perplexity may fall back to\n * anonymous search exactly like the retired single-preference setting did.\n */","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/provider.ts#L167-L203","documentation":"getSearchProvider resolves a provider id (e.g. 'brave', 'anthropic') from the PROVIDER_META registry, instantiates and caches it. If the id is not a key of PROVIDER_META it throws 'Unknown search provider: <id>'. This is an invalid-identifier error protecting against typos and stale provider ids.","triggerScenarios":"Passing an unregistered id (typo, removed/renamed provider, user-supplied string from config) to getSearchProvider or as the explicit provider in runSearchQuery.","commonSituations":"Typo in config ('Brave' vs 'brave'); provider removed after an upgrade so old configs reference a deleted id; programmatically building ids from unvalidated user input.","solutions":["Use a valid provider id from the SearchProviderId type / PROVIDER_META keys.","Validate user/config-supplied provider names against the allowed id list before calling.","Update configs after upgrades that rename or remove providers.","Narrow with the SearchProviderId type so invalid ids are caught at compile time."],"exampleFix":"// before\nconst p = await getSearchProvider(userInput.provider); // 'google' not registered\n// after\nconst VALID = [\"brave\", \"anthropic\"] as const;\nif (!VALID.includes(userInput.provider as never)) {\n  throw new Error(`provider must be one of: ${VALID.join(\", \")}`);\n}\nconst p = await getSearchProvider(userInput.provider as SearchProviderId);","handlingStrategy":"validation","validationCode":"const PROVIDER_IDS = [\"brave\", \"anthropic\"] as const; // mirror SearchProviderId\ntype ProviderId = (typeof PROVIDER_IDS)[number];\nfunction isValidProviderId(x: string): x is ProviderId {\n  return (PROVIDER_IDS as readonly string[]).includes(x);\n}\nif (!isValidProviderId(config.provider)) throw new Error(`Unknown provider: ${config.provider}`);","typeGuard":"function isSearchProviderId(x: string): x is SearchProviderId {\n  return x in PROVIDER_META; // same registry the loader consults\n}","tryCatchPattern":"try { const p = await getSearchProvider(id); }\ncatch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Unknown search provider:\")) {\n    return { content: [{ type: \"text\", text: `Provider '${id}' is not available. Valid: brave, anthropic` }] };\n  }\n  throw e;\n}","preventionTips":["Type provider ids as SearchProviderId instead of string wherever possible.","Validate config/user input against the registry keys before calling.","After upgrading, migrate configs that reference removed provider ids.","Use lowercase canonical ids; normalize user input before lookup."],"tags":["configuration","invalid-argument","search-provider"],"backgroundTag":"invalid-enum-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}