{"record":{"id":"a6ca1bcfbee3ffb6","repo":"different-ai/openwork","slug":"failed-to-load-providers-response-status","errorCode":null,"errorMessage":"Failed to load providers (${response.status}).","messagePattern":"Failed to load providers \\((.+?)\\)\\.","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/llm-provider-data.tsx","lineNumber":524,"sourceCode":"  const scope = options.scope ?? \"manageable\";\n\n  async function loadProviders() {\n    if (!orgId) {\n      setLlmProviders([]);\n      setError(\"Organization not found.\");\n      return;\n    }\n\n    setBusy(true);\n    setError(null);\n    try {\n      const { response, payload } = await requestJson(\n        `/v1/llm-providers?scope=${encodeURIComponent(scope)}`,\n        { method: \"GET\" },\n        15000,\n      );\n      if (!response.ok) {\n        throw new Error(getErrorMessage(payload, `Failed to load providers (${response.status}).`));\n      }\n\n      const nextProviders = isRecord(payload) && Array.isArray(payload.llmProviders)\n        ? payload.llmProviders.map(asLlmProvider).filter((entry): entry is DenLlmProvider => entry !== null)\n        : [];\n      setLlmProviders(nextProviders);\n    } catch (loadError) {\n      setError(loadError instanceof Error ? loadError.message : \"Failed to load the provider library.\");\n    } finally {\n      setBusy(false);\n    }\n  }\n\n  useEffect(() => {\n    void loadProviders();\n  }, [orgId, scope]);\n\n  return {","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/llm-provider-data.tsx#L506-L542","documentation":"loadProviders, inside the useOrgLlmProviders hook, GETs /v1/llm-providers?scope=...; a non-ok response throws an Error preferring the server message with the status embedded. React state (setLlmProviders) is only updated after this check, so failed loads keep prior state and surface the error to the caller.","triggerScenarios":"GET /v1/llm-providers returns 401 (expired session), 403 (insufficient org role), 404 (route/server version mismatch), 429, or 5xx; an invalid scope value causing a 400 from the server.","commonSituations":"Session expiring while the providers panel is mounted; user without admin permissions viewing org providers; frontend deployed against an older den-api without this endpoint; malformed scope argument passed into the hook.","solutions":["Inspect response.status and the server message to classify the failure (auth vs permission vs route vs server).","For 401, refresh the session or redirect to sign-in; for 403, verify the user's org role.","For 404, align frontend and backend deployments.","Validate the `scope` value passed to useOrgLlmProviders against the API's accepted values.","Check den-api logs for 5xx root causes and retry after recovery."],"exampleFix":"// before\nthrow new Error(getErrorMessage(payload, `Failed to load providers (${response.status}).`));\n// after\nif (response.status === 403) throw new Error(\"You do not have permission to view providers for this organization.\");\nthrow new Error(getErrorMessage(payload, `Failed to load providers (${response.status}).`));","handlingStrategy":"try-catch","validationCode":"const VALID_SCOPES = [\"org\", \"user\"] as const;\n// guard before the query runs\nif (!(VALID_SCOPES as readonly string[]).includes(scope)) throw new Error(`Invalid provider scope: ${scope}`);","typeGuard":"function isProviderScope(v: string): v is \"org\" | \"user\" {\n  return v === \"org\" || v === \"user\";\n}","tryCatchPattern":"const { error } = useOrgLlmProviders(scope);\nif (error) {\n  const msg = error instanceof Error ? error.message : String(error);\n  if (/\\((401|403)\\)/.test(msg)) handleAuthOrPermission(msg);\n  else setProviderLoadError(msg);\n}","preventionTips":["Type the scope parameter to a union instead of a raw string.","Wire React Query retry only for 5xx/timeouts, not 4xx.","Check org role before mounting provider admin panels.","Keep a global 401 interceptor that refreshes the session and retries queries once."],"tags":["network","http-status","react-query","llm-provider","den-web"],"backgroundTag":"http-request-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}