{"record":{"id":"a15c2c3042ba2185","repo":"moeru-ai/airi","slug":"failed-to-fetch-providers","errorCode":null,"errorMessage":"Failed to fetch providers","messagePattern":"Failed to fetch providers","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/services/inference-service-providers.ts","lineNumber":136,"sourceCode":"    let status: ProviderValidationStatus = 'unconfigured'\n    if (item.validated)\n      status = 'configured'\n    else if (item.validationBypassed)\n      status = 'bypassed'\n\n    return {\n      id: item.id,\n      definitionId: item.definitionId,\n      config: item.config,\n      status,\n    }\n  }\n\n  async function fetchRemote(client: InferenceServiceProvidersRemoteClient, options?: InferenceServiceProviderServiceOptions): Promise<InferenceServiceProviders> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.providers.$get(undefined, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to fetch providers')\n\n    const data = await res.json() as unknown[]\n    options?.abortSignal?.throwIfAborted()\n\n    const providers: InferenceServiceProviders = {}\n    for (const item of data) {\n      const provider = normalize(item)\n      providers[provider.id] = provider\n    }\n    return providers\n  }\n\n  async function createRemote(client: InferenceServiceProvidersRemoteClient, provider: InferenceServiceProvider, options?: InferenceServiceProviderServiceOptions): Promise<InferenceServiceProvider> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.providers.$post({\n      json: {\n        id: provider.id,\n        definitionId: provider.definitionId,","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/services/inference-service-providers.ts#L118-L154","documentation":"Thrown by InferenceServiceProvidersService.fetchRemote when GET /v1/providers returns non-ok. The service then maps the returned array into an InferenceServiceProviders record via normalize. Failure aborts before normalization, so the caller receives no providers.","triggerScenarios":"client.api.v1.providers.$get() resolves with ok=false. Typical: 401/403 (not authenticated), 500 (server), network-level failure mapped to non-ok, /v1/providers route not deployed.","commonSituations":"Logged-out session loading the providers settings page; backend down or misconfigured; wrong SERVER_URL; expired JWT; CORS blocking the response.","solutions":["Confirm authentication and JWT validity before calling fetchRemote.","Verify SERVER_URL points at a backend serving /v1/providers.","Include res.status in the message for diagnosis.","On failure, fall back to locally stored providers or show an empty state with retry."],"exampleFix":"// before\nif (!res.ok)\n  throw new Error('Failed to fetch providers')\n\n// after\nif (!res.ok)\n  throw new Error(`Failed to fetch providers (status ${res.status})`)","handlingStrategy":"try-catch","validationCode":"function isAuthenticated(): boolean {\n  return !!getAuthToken()\n}\n\nif (!isAuthenticated()) {\n  // redirect to login or return local providers\n  return {}\n}\nawait fetchRemote(client)","typeGuard":null,"tryCatchPattern":"try {\n  return await fetchRemote(client)\n}\ncatch (error) {\n  // Fall back to locally stored providers or show empty state with retry.\n  return localProviders\n}","preventionTips":["Confirm authentication and SERVER_URL before calling fetchRemote.","Patch the service to embed res.status in the message for diagnosis.","Cache the last successful provider list to degrade gracefully."],"tags":["network","http","providers","api","auth"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}