{"record":{"id":"f4c61144f8cfd6c7","repo":"different-ai/openwork","slug":"failed-to-revoke-access-response-status","errorCode":null,"errorMessage":"Failed to revoke access (${response.status}).","messagePattern":"Failed to revoke access \\((.+?)\\)\\.","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/marketplace-data.tsx","lineNumber":324,"sourceCode":"      queryClient.invalidateQueries({ queryKey: marketplaceQueryKeys.resolved(marketplaceId) });\n    },\n  });\n}\n\nexport function useRevokeMarketplaceAccess() {\n  const queryClient = useQueryClient();\n  const { runReauthableAction } = useOrgDashboard();\n\n  return useMutation({\n    mutationFn: async (input: { marketplaceId: string; grantId: string }) => {\n      await runReauthableAction(\"revoke-marketplace-access\", async () => {\n      const { response, payload } = await requestJson(\n        `/v1/marketplaces/${encodeURIComponent(input.marketplaceId)}/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 access (${response.status}).`);\n      }\n      });\n      return input.marketplaceId;\n    },\n    onSuccess: (marketplaceId) => {\n      queryClient.invalidateQueries({ queryKey: marketplaceQueryKeys.access(marketplaceId) });\n      queryClient.invalidateQueries({ queryKey: marketplaceQueryKeys.resolved(marketplaceId) });\n    },\n  });\n}\n\nexport type ConfigurePluginMcpConnectionInput = {\n  pluginId: string;\n  configObjectId: string;\n  serverName: string;\n  authType: ExternalMcpAuthType;\n  credentialMode: ExternalMcpCredentialMode;\n  apiKey?: string;","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/marketplace-data.tsx#L306-L342","documentation":"Thrown by useRevokeMarketplaceAccess in marketplace-data.tsx when DELETE /v1/marketplaces/{marketplaceId}/access/{grantId} (15s timeout) returns neither 204 nor ok. Like the grant path, the error is built by getRequestError (den-flow.ts:527): ReauthRequiredError on 403 {error:'reauth'}, else the server `error` message or fallback \"Failed to revoke access (<status>).\".","triggerScenarios":"DELETE returns non-ok: 401/403 (caller lacks owner/admin rights or session expired), 404 (grant id already revoked or marketplace removed), 409/422 (grant is the caller's own access or tied to active entitlements), 5xx, or the 15s timeout.","commonSituations":"Two admins revoking the same grant simultaneously so the second gets 404; revoking your own access which the server forbids; stale grant list after the marketplace was deleted; expired session in a long-open dashboard.","solutions":["On 404, treat as already revoked: invalidate marketplaceQueryKeys.access(marketplaceId) and remove the row locally.","For 403 reauth responses, detect isReauthRequiredError(error) and start the sign-in flow before retrying.","If the server blocks self-revocation, have another admin perform the revoke.","On 5xx/timeout, retry the idempotent DELETE once."],"exampleFix":"// before\nif (response.status !== 204 && !response.ok) {\n  throw getRequestError(payload, response, `Failed to revoke access (${response.status}).`);\n}\n// after\nif (response.status !== 204 && !response.ok) {\n  if (response.status === 404) return input.marketplaceId; // already revoked\n  throw getRequestError(payload, response, `Failed to revoke access (${response.status}).`);\n}","handlingStrategy":"try-catch","validationCode":"// before revoking\nif (!grantId) throw new Error(\"grantId is required.\");\nif (grant.granteeId === currentUserId && !allowSelfRevoke) {\n  throw new Error(\"Ask another admin to revoke your access.\");\n}","typeGuard":"function isReauthError(e: unknown): e is ReauthRequiredError {\n  return e instanceof ReauthRequiredError;\n}","tryCatchPattern":"try {\n  await revokeMutation.mutateAsync({ marketplaceId, grantId });\n} catch (error) {\n  if (isReauthError(error)) { startReauth(); return; }\n  if (/\\(404\\)/.test(error.message)) { await queryClient.invalidateQueries(marketplaceQueryKeys.access(marketplaceId)); return; }\n  showToast(error.message);\n}","preventionTips":["Treat 404 as already-revoked and just refresh the access list.","Refresh the grant list before revoking to drop stale grant ids.","Block self-revocation client-side when the server forbids it.","Gate revoke controls on owner/admin role and confirm with a dialog."],"tags":["http","marketplace","den-api","access-control"],"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"}