{"record":{"id":"3d1084aa5fb89419","repo":"different-ai/openwork","slug":"failed-to-load-plugin-access-response-status","errorCode":null,"errorMessage":"Failed to load plugin access (${response.status}).","messagePattern":"Failed to load plugin access \\((.+?)\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/plugin-access-data.tsx","lineNumber":90,"sourceCode":"    role,\n    createdByOrgMembershipId,\n    createdAt,\n    removedAt,\n  };\n}\n\nexport function usePluginAccess(pluginId: string) {\n  return useQuery({\n    enabled: Boolean(pluginId),\n    queryKey: pluginAccessQueryKeys.detail(pluginId),\n    queryFn: async (): Promise<PluginAccessGrant[]> => {\n      const { response, payload } = await requestJson(\n        `/v1/plugins/${encodeURIComponent(pluginId)}/access`,\n        { method: \"GET\" },\n        15000,\n      );\n      if (!response.ok) {\n        throw new Error(getErrorMessage(payload, `Failed to load plugin access (${response.status}).`));\n      }\n      const items = isRecord(payload) && Array.isArray(payload.items) ? payload.items : [];\n      return items\n        .map(parsePluginAccessGrant)\n        .filter((grant): grant is PluginAccessGrant => grant !== null && grant.removedAt === null);\n    },\n  });\n}\n\ntype GrantPluginAccessBody =\n  | { orgMembershipId: string; teamId?: never; orgWide?: never; role: PluginAccessRole }\n  | { orgMembershipId?: never; teamId: string; orgWide?: never; role: PluginAccessRole }\n  | { orgMembershipId?: never; teamId?: never; orgWide: true; role: PluginAccessRole };\n\nexport function useGrantPluginAccess() {\n  const queryClient = useQueryClient();\n  const { runReauthableAction } = useOrgDashboard();\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/plugin-access-data.tsx#L72-L108","documentation":"usePluginAccess fetches GET /v1/plugins/:id/access from the Den API. When the HTTP response status is not 2xx it throws this error, preferring a server-provided message extracted from the error payload via getErrorMessage, with the status-embedded fallback string if the payload has no usable message. It surfaces through TanStack Query as the query's error.","triggerScenarios":"The GET /v1/plugins/{pluginId}/access request returns 401/403 (missing or expired session, no org membership), 404 (pluginId does not exist or is not visible to the caller), 5xx (Den server/DB failure), or a network-level failure yields an error status from requestJson.","commonSituations":"An expired Den session cookie after idle timeout; a plugin removed by an org admin while a dashboard tab is still open; pointing den-web at a server that lacks the plugin access endpoint (version mismatch); an org slug/plugin id pasted from another org.","solutions":["Sign in again / refresh the session to clear 401/403, then reload the dashboard page.","Verify the pluginId is valid for the current org by loading /v1/plugins/{id} directly (curl or browser devtools network tab).","Confirm the Den server version supports the plugin access endpoint; upgrade the self-hosted server if 404/405 persists.","Check Den server logs for 5xx causes (DB connectivity, migration lag) if the status is 500/502/503."],"exampleFix":"// before: raw queryFn throwing with opaque fallback\nthrow new Error(getErrorMessage(payload, `Failed to load plugin access (${response.status}).`));\n// after: handle 401 by re-authenticating instead of throwing\nif (response.status === 401) {\n  await redirectToSignIn();\n  return [];\n}\nif (!response.ok) {\n  throw new Error(getErrorMessage(payload, `Failed to load plugin access (${response.status}).`));\n}","handlingStrategy":"try-catch","validationCode":"const pluginIdOk = typeof pluginId === \"string\" && pluginId.length > 0;\nconst sessionOk = document.cookie.includes(\"den-session\") || Boolean(await getSession());\nif (!pluginIdOk || !sessionOk) { await redirectToSignIn(); }","typeGuard":"function hasUsableErrorPayload(p: unknown): p is { message: string } {\n  return typeof p === \"object\" && p !== null && typeof (p as { message?: unknown }).message === \"string\";\n}","tryCatchPattern":"try {\n  const access = await pluginAccessQuery(pluginId);\n} catch (err) {\n  const status = /\\((\\d{3})\\)\\.$/.exec(err instanceof Error ? err.message : \"\")?.[1];\n  if (status === \"401\") { await redirectToSignIn(); }\n  else if (status === \"404\") { showNotFound(pluginId); }\n  else { showToast(`Could not load plugin access${status ? ` (${status})` : \"\"}; retry later.`); }\n}","preventionTips":["Check session validity before org-scoped Den API calls and redirect to sign-in on 401.","Validate pluginId is a non-empty string before building the request URL.","Render the access section with an empty/error state so one failed query doesn't blank the dashboard.","Configure TanStack Query retry only for transient statuses (5xx/timeout), not 401/403/404."],"tags":["http","api-client","den-web","plugin-access"],"backgroundTag":"http-request-failed-with-status","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}