{"record":{"id":"37af42adadce2869","repo":"different-ai/openwork","slug":"failed-to-revoke-plugin-access-response-status","errorCode":null,"errorMessage":"Failed to revoke plugin access (${response.status}).","messagePattern":"Failed to revoke plugin access \\((.+?)\\)\\.","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/plugin-access-data.tsx","lineNumber":142,"sourceCode":"      queryClient.invalidateQueries({ queryKey: pluginAccessQueryKeys.detail(pluginId) });\n    },\n  });\n}\n\nexport function useRevokePluginAccess() {\n  const queryClient = useQueryClient();\n  const { runReauthableAction } = useOrgDashboard();\n\n  return useMutation({\n    mutationFn: async (input: { pluginId: string; grantId: string }) => {\n      await runReauthableAction(\"revoke-plugin-access\", async () => {\n        const { response, payload } = await requestJson(\n          `/v1/plugins/${encodeURIComponent(input.pluginId)}/access/${encodeURIComponent(input.grantId)}`,\n          { method: \"DELETE\" },\n          15000,\n        );\n        if (response.status !== 204 && !response.ok) {\n          throw getRequestError(payload, response, `Failed to revoke plugin access (${response.status}).`);\n        }\n      });\n      return input.pluginId;\n    },\n    onSuccess: (pluginId) => {\n      queryClient.invalidateQueries({ queryKey: pluginAccessQueryKeys.detail(pluginId) });\n    },\n  });\n}\n","sourceCodeStart":124,"sourceCodeEnd":152,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/plugin-access-data.tsx#L124-L152","documentation":"Thrown by useRevokePluginAccess when DELETE /v1/plugins/:id/access/:grantId returns neither 204 nor ok. The message embeds the HTTP status unless the payload supplies a server error message. 403 'reauth' payloads surface as ReauthRequiredError instead.","triggerScenarios":"Revoking an access grant that no longer exists (404 — concurrent revocation or stale list), lacking permission to manage plugin access (403), expired session (401), malformed grantId, or server error.","commonSituations":"Stale access list where the grant was already removed; admin permissions changed mid-session; plugin itself deleted while its access list was still open.","solutions":["On 404, refetch pluginAccessQueryKeys.detail(pluginId) and treat the grant as already revoked.","On 401/403, re-authenticate or verify access-management rights.","On 5xx, retry after confirming current grant state to avoid acting on stale data.","Validate grantId came from the latest access list response before calling revoke."],"exampleFix":"// before\nif (response.status !== 204 && !response.ok) {\n  throw getRequestError(payload, response, `Failed to revoke plugin access (${response.status}).`);\n}\n// after\nif (response.status === 404) return input.pluginId; // already revoked\nif (response.status !== 204 && !response.ok) {\n  throw getRequestError(payload, response, `Failed to revoke plugin access (${response.status}).`);\n}","handlingStrategy":"try-catch","validationCode":"if (!input.pluginId || !input.grantId || typeof input.grantId !== \"string\") {\n  throw new Error(\"A valid pluginId and grantId are required to revoke access.\");\n}","typeGuard":"function isGrantId(value: unknown): value is string {\n  return typeof value === \"string\" && value.length > 0;\n}","tryCatchPattern":"try {\n  await revokePluginAccess.mutateAsync({ pluginId, grantId });\n} catch (error) {\n  if (isReauthRequiredError(error)) return startReauth();\n  if (error.message.includes(\"(404)\")) return; // already revoked\n  toast.error(error.message);\n} finally {\n  await queryClient.invalidateQueries({ queryKey: pluginAccessQueryKeys.detail(pluginId) });\n}","preventionTips":["Treat 404 on revoke as already-revoked success and refetch.","Invalidate pluginAccessQueryKeys.detail(pluginId) after every revoke attempt.","Use grantIds from the latest access list response only.","Hide revoke actions for users without access-management permission."],"tags":["http","api","plugin","access-control","react-query"],"backgroundTag":"http-non-ok-response","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}