aaif-goose/goose · error
Unknown provider: ${providerId}
Error message
Unknown provider: ${providerId} What it means
Thrown by acpGetProviderDetails: it sends providersList_unstable({providerIds: [providerId]}) and searches response.entries for a matching providerId. The goose backend returns an entry only for ids it knows (built-in or registered providers), so a miss means the id does not exist in the backend's provider inventory — different from 'not configured'. This is a client-side guard before providerEntryToDetails would dereference undefined.
Source
Thrown at ui/desktop/src/acp/providers.ts:120
const { entries } = await client.goose.providersList_unstable({});
return entries.map(providerEntryToDetails);
}
export async function acpListSetupProviderDetails(): Promise<ProviderDetails[]> {
const providers = await acpListProviderDetails();
return providers.filter((provider) => provider.visible_in_setup);
}
export async function acpListSettingsProviderDetails(): Promise<ProviderDetails[]> {
const providers = await acpListProviderDetails();
return providers.filter((provider) => provider.visible_in_setup || provider.is_configured);
}
export async function acpGetProviderDetails(providerId: string): Promise<ProviderDetails> {
const client = await getAcpClient();
const { entries } = await client.goose.providersList_unstable({ providerIds: [providerId] });
const entry = entries.find((candidate) => candidate.providerId === providerId);
if (!entry) throw new Error(`Unknown provider: ${providerId}`);
return providerEntryToDetails(entry);
}
async function waitForProviderInventoryRefresh(
client: Awaited<ReturnType<typeof getAcpClient>>,
providerId: string,
refresh: RefreshProviderInventoryResponse_unstable,
signal?: globalThis.AbortSignal
): Promise<ProviderDetails> {
const shouldWait =
refresh.started.includes(providerId) ||
refresh.skipped?.some(
(skip) => skip.providerId === providerId && skip.reason === 'already_refreshing'
);
let entry: ProviderInventoryEntryDto | undefined;
const attempts = shouldWait
? INVENTORY_REFRESH_TIMEOUT_MS / INVENTORY_REFRESH_POLL_INTERVAL_MSView on GitHub (pinned to 3810898a74)
Solutions
- Call acpListProviderDetails() (or acpListSettingsProviderDetails) and print the available providerIds — use an exact id from that list.
- Check for trailing spaces/case mismatch in the id; the find is an exact === on providerId.
- If the id came from persisted settings, clear or migrate it after a goose upgrade that renamed providers.
- Confirm the backend version: providersList_unstable is an _unstable API and its inventory can change between releases.
Example fix
// before
const { entries } = await client.goose.providersList_unstable({ providerIds: [providerId] });
const entry = entries.find((candidate) => candidate.providerId === providerId);
if (!entry) throw new Error(`Unknown provider: ${providerId}`);
// after (fail with the ids that do exist)
const { entries } = await client.goose.providersList_unstable({ providerIds: [providerId] });
const entry = entries.find((candidate) => candidate.providerId === providerId);
if (!entry) {
const all = await client.goose.providersList_unstable({ providerIds: [] });
throw new Error(`Unknown provider: ${providerId}. Available: ${all.entries.map((e) => e.providerId).join(', ')}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
// Resolve the id against the live inventory before fetching details
const known = await acpListProviderDetails();
const validId = known.find((p) => p.id === providerId)?.id;
if (!validId) {
throw new Error(`Provider '${providerId}' is not in the inventory. Available: ${known.map((p) => p.id).join(', ')}`);
} Type guard
async function isKnownProvider(providerId: string): Promise<boolean> {
const providers = await acpListProviderDetails();
return providers.some((p) => p.id === providerId);
} Try / catch
try {
const details = await acpGetProviderDetails(providerId);
} catch (error) {
if (/Unknown provider/.test(String(error))) {
return refreshProviderListUi(); // repopulate picker from the inventory
}
throw error;
} Prevention
- Always populate provider pickers from providersList_unstable output, never from a hardcoded list.
- Trim whitespace and match casing exactly — the lookup is a strict === on providerId.
- Drop persisted provider ids after goose upgrades that rename providers.
When it happens
Trigger: Calling acpGetProviderDetails('openai ') with a typo or whitespace; an id from an older/newer goose version (provider renamed or removed); a custom provider not yet registered in the profile; entries returned under a different providerId spelling (case-sensitive compare).
Common situations: Persisted provider id in settings/localStorage after upgrading goose; hand-edited config referencing a deleted provider; UI list rendered from a cached inventory while the backend inventory changed.
Related errors
- Unsupported extension type for ACP: ${config.type}
- External ACP backend URL is required
- Some providers in skip list don't exist
- Cannot resume with provider or model changes because provide
- Invalid provider id: provider id cannot be empty
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/15f997031d983f1f.
Report an issue: GitHub.