different-ai/openwork · error
Failed to grant plugin access (${response.status}).
Error message
Failed to grant plugin access (${response.status}). What it means
Thrown by useGrantPluginAccess when POST /v1/plugins/:id/access returns non-ok. getRequestError prefers the server's error message; otherwise the fallback with the HTTP status is used. A 403 'reauth' payload becomes ReauthRequiredError.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/plugin-access-data.tsx:118
type GrantPluginAccessBody =
| { orgMembershipId: string; teamId?: never; orgWide?: never; role: PluginAccessRole }
| { orgMembershipId?: never; teamId: string; orgWide?: never; role: PluginAccessRole }
| { orgMembershipId?: never; teamId?: never; orgWide: true; role: PluginAccessRole };
export function useGrantPluginAccess() {
const queryClient = useQueryClient();
const { runReauthableAction } = useOrgDashboard();
return useMutation({
mutationFn: async (input: { pluginId: string; body: GrantPluginAccessBody }) => {
await runReauthableAction("grant-plugin-access", async () => {
const { response, payload } = await requestJson(
`/v1/plugins/${encodeURIComponent(input.pluginId)}/access`,
{ method: "POST", body: JSON.stringify(input.body) },
15000,
);
if (!response.ok) {
throw getRequestError(payload, response, `Failed to grant plugin access (${response.status}).`);
}
});
return input.pluginId;
},
onSuccess: (pluginId) => {
queryClient.invalidateQueries({ queryKey: pluginAccessQueryKeys.detail(pluginId) });
},
});
}
export function useRevokePluginAccess() {
const queryClient = useQueryClient();
const { runReauthableAction } = useOrgDashboard();
return useMutation({
mutationFn: async (input: { pluginId: string; grantId: string }) => {
await runReauthableAction("revoke-plugin-access", async () => {
const { response, payload } = await requestJson(View on GitHub (pinned to 2b7df46e8a)
Solutions
- Check the status: 400 → validate the access body (subject and role) before sending; 404 → refresh pluginQueryKeys.all, the plugin was removed.
- On 401/403, re-authenticate or confirm sharing permissions; handle ReauthRequiredError explicitly.
- Invalidate pluginAccessQueryKeys.detail(pluginId) and refetch before retrying to avoid duplicates.
- Resolve any payment_required gate reported in the server message.
Example fix
// before
throw getRequestError(payload, response, `Failed to grant plugin access (${response.status}).`);
// after
if (isReauthRequiredError(await Promise.resolve())) return; // pattern: catch and branch
const err = getRequestError(payload, response, `Failed to grant plugin access (${response.status}).`);
if (isReauthRequiredError(err)) return startReauth();
throw err; Defensive patterns
Strategy: validation
Validate before calling
if (!input.pluginId || typeof input.pluginId !== "string") throw new Error("pluginId is required.");
if (!isValidAccessBody(input.body)) throw new Error("Select a valid subject and role before granting plugin access."); Type guard
function isValidAccessBody(body: unknown): body is { subjectId: string; role: string } {
return typeof body === "object" && body !== null
&& typeof (body as { subjectId?: unknown }).subjectId === "string"
&& typeof (body as { role?: unknown }).role === "string";
} Try / catch
try {
await grantPluginAccess.mutateAsync({ pluginId, body });
} catch (error) {
if (isReauthRequiredError(error)) return startReauth();
toast.error(error.message);
await queryClient.invalidateQueries({ queryKey: pluginAccessQueryKeys.detail(pluginId) });
} Prevention
- Confirm the plugin still exists (fresh pluginQueryKeys.detail) before opening the share panel.
- Validate the access body shape client-side before POSTing.
- Handle ReauthRequiredError in every plugin-access mutation.
- Refetch the access detail before retrying to avoid duplicate grants.
When it happens
Trigger: Granting plugin access with an invalid body (unknown subject ID or role — 400), plugin not found (404), insufficient permission to share the plugin (403), expired token (401), or org plugin-access limits/payment_required.
Common situations: Sharing a plugin with a member removed from the org, an unpublished or deleted plugin still cached in the UI, or role enum drift between client and API versions.
Related errors
- Failed to revoke plugin access (${response.status}).
- Failed to grant dashboard access (${response.status}).
- Failed to revoke dashboard access (${response.status}).
- Failed to update plugin (${response.status}).
- Failed to load dashboards (${response.status}).
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/c5e433a16ced9b60.
Report an issue: GitHub.