different-ai/openwork · error

Failed to grant dashboard access (${response.status}).

Error message

Failed to grant dashboard access (${response.status}).

What it means

Thrown by useGrantDashboardAccess when POST /v1/dashboards/:id/access returns non-ok. The fallback message carries the HTTP status; a server-provided error message overrides it. A 403 'reauth' payload is raised as ReauthRequiredError instead.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/org-dashboards-data.tsx:335

type GrantDashboardAccessBody =
  | { orgMembershipId: string; teamId?: never; orgWide?: never; role: DashboardAccessRole }
  | { orgMembershipId?: never; teamId: string; orgWide?: never; role: DashboardAccessRole }
  | { orgMembershipId?: never; teamId?: never; orgWide: true; role: DashboardAccessRole };

export function useGrantDashboardAccess() {
  const queryClient = useQueryClient();
  const { orgContext, runReauthableAction } = useOrgDashboard();
  const organizationId = orgContext?.organization.id ?? "";
  return useMutation({
    mutationFn: async (input: { dashboardId: string; body: GrantDashboardAccessBody }) => {
      await runReauthableAction("grant-dashboard-access", async () => {
        const { response, payload } = await requestJson(
          `/v1/dashboards/${encodeURIComponent(input.dashboardId)}/access`,
          { method: "POST", body: JSON.stringify(input.body) },
          15000,
        );
        if (!response.ok) {
          throw getRequestError(payload, response, `Failed to grant dashboard access (${response.status}).`);
        }
      });
      return input.dashboardId;
    },
    onSuccess: (dashboardId) => {
      queryClient.invalidateQueries({ queryKey: orgDashboardsQueryKeys.access(organizationId, dashboardId) });
    },
  });
}

export function useRevokeDashboardAccess() {
  const queryClient = useQueryClient();
  const { orgContext, runReauthableAction } = useOrgDashboard();
  const organizationId = orgContext?.organization.id ?? "";
  return useMutation({
    mutationFn: async (input: { dashboardId: string; grantId: string }) => {
      await runReauthableAction("revoke-dashboard-access", async () => {
        const { response, payload } = await requestJson(

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Read the status: 400 → validate the access body (member/group IDs and role) against the current API schema; 404 → refresh the member list, the grantee no longer exists.
  2. On 401/403, re-authenticate or confirm the caller has dashboard-sharing rights.
  3. Invalidate orgDashboardsQueryKeys.access and refetch before retrying to avoid duplicate grants.
  4. Check the thrown message for payment_required and resolve the org billing gate.

Example fix

// before
await requestJson(`/v1/dashboards/${encodeURIComponent(input.dashboardId)}/access`, { method: "POST", body: JSON.stringify(input.body) }, 15000);
// after
if (!input.body || typeof input.body.role !== "string" || !input.body.subjectId) {
  throw new Error("Select a valid member and role before granting access.");
}
await requestJson(`/v1/dashboards/${encodeURIComponent(input.dashboardId)}/access`, { method: "POST", body: JSON.stringify(input.body) }, 15000);
Defensive patterns

Strategy: validation

Validate before calling

if (!input.body?.subjectId || typeof input.body.role !== "string" || input.body.role.length === 0) {
  throw new Error("Select a valid member/group and role before granting dashboard 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 grantAccess.mutateAsync({ dashboardId, body });
} catch (error) {
  if (isReauthRequiredError(error)) return startReauth();
  toast.error(error.message);
  await queryClient.invalidateQueries({ queryKey: orgDashboardsQueryKeys.access(organizationId, dashboardId) });
}

Prevention

When it happens

Trigger: Granting access with a body the server rejects (unknown member/group ID, invalid role enum — 400), target grantee not in the org (404), caller lacks share permissions (403), expired token (401), or payment/seat gating on the workspace.

Common situations: Sharing a dashboard with a user who was removed from the org, selecting a role value not supported by the current API version, or stale member list in the UI.

Related errors


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