different-ai/openwork · error

Failed to revoke access (${response.status}).

Error message

Failed to revoke access (${response.status}).

What it means

Thrown by useRevokeTeamPluginAccess when DELETE /v1/plugins/{pluginId}/access/{grantId} returns a status other than 204 or generic non-ok. This removes a team's granted access to a plugin; on success the team-access detail query is invalidated. Like the other den-web mutations, getRequestError appends the server message and converts 403 error:'reauth' into ReauthRequiredError.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/team-access-data.tsx:146

        .filter((item): item is TeamPluginAccessItem => item !== null);
    },
  });
}

export function useRevokeTeamPluginAccess() {
  const queryClient = useQueryClient();
  const { runReauthableAction } = useOrgDashboard();

  return useMutation({
    mutationFn: async (input: { teamId: string; pluginId: string; grantId: string }) => {
      await runReauthableAction("revoke-team-plugin-access", async () => {
        const { response, payload } = await requestJson(
          `/v1/plugins/${encodeURIComponent(input.pluginId)}/access/${encodeURIComponent(input.grantId)}`,
          { method: "DELETE" },
          15000,
        );
        if (response.status !== 204 && !response.ok) {
          throw getRequestError(payload, response, `Failed to revoke access (${response.status}).`);
        }
      });
      return input.teamId;
    },
    onSuccess: (teamId) => {
      queryClient.invalidateQueries({ queryKey: teamAccessQueryKeys.detail(teamId) });
    },
  });
}

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Read the appended server message to distinguish 403 (permissions) vs 404 (already revoked).
  2. On 404, treat the grant as already revoked: invalidate teamAccessQueryKeys.detail(teamId) and refresh the UI.
  3. Handle ReauthRequiredError (sign-in) and retry the revoke.
  4. Verify the caller still has plugin/org admin rights for 403.
  5. Retry on 429/5xx with backoff; check Den server health for persistent failures.

Example fix

// before
revokeAccess.mutate(input, { onError: (e) => toast(e.message) });
// after: tolerate already-revoked
revokeAccess.mutate(input, {
  onError: (e) => {
    if (/\b404\b/.test(e.message)) {
      queryClient.invalidateQueries({ queryKey: teamAccessQueryKeys.detail(input.teamId) });
      return;
    }
    toast(e.message);
  },
});
Defensive patterns

Strategy: try-catch

Validate before calling

if (!input.pluginId || !input.grantId) throw new Error("pluginId and grantId are required to revoke access.");

Type guard

function isReauthRequiredError(e: unknown): e is ReauthRequiredError {
  return e instanceof ReauthRequiredError;
}

Try / catch

try {
  await revokeAccess.mutateAsync(input);
} catch (err) {
  if (isReauthRequiredError(err)) { promptSignIn(); return; }
  if (/\b404\b/.test(err.message)) { // grant already revoked elsewhere
    queryClient.invalidateQueries({ queryKey: teamAccessQueryKeys.detail(input.teamId) });
    return;
  }
  showError(err.message);
}

Prevention

When it happens

Trigger: DELETE /v1/plugins/{pluginId}/access/{grantId} returns 401 (expired session), 403 (no admin rights over the plugin/org, or reauth challenge), 404 (pluginId or grantId no longer exists — grant already revoked elsewhere), 409 (grant is locked, e.g. required by an assignment policy), 429, or 5xx. 15s timeout.

Common situations: Two admins revoke the same grant concurrently (second gets 404); the plugin was deleted leaving stale grant ids in the UI; role downgraded mid-session; long-idle tab with expired token.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/a8080b48b8862de6. Report an issue: GitHub.