different-ai/openwork · error · Error

Failed to load MCP connectors (${response.status}).

Error message

Failed to load MCP connectors (${response.status}).

What it means

Thrown by fetchConnections (the queryFn of useMcpConnections) when GET /v1/mcp-connections?scope=<scope> returns a non-ok status (15000ms timeout). getRequestError surfaces the server's error message or 'Failed to load MCP connectors (<status>)'. The hook then surfaces the error via TanStack Query's error state instead of returning a connection list.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-data.tsx:558

  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
}

function parseRequiredBy(value: unknown): ExternalMcpRequiredBy[] {
  if (!Array.isArray(value)) return [];
  return value.flatMap((entry) => {
    if (!isRecord(entry) || typeof entry.pluginId !== "string" || typeof entry.name !== "string") return [];
    return [{ pluginId: entry.pluginId, name: entry.name }];
  });
}

async function fetchConnections(scope: ExternalMcpConnectionScope, orgId: string): Promise<ExternalMcpConnection[]> {
  const { response, payload } = await requestJson(
    `/v1/mcp-connections?scope=${scope}`,
    { headers: getOrgScopeHeaders(orgId) },
    15000,
  );
  if (!response.ok) {
    throw getRequestError(payload, response, `Failed to load MCP connectors (${response.status}).`);
  }
  const record = payload as { connections?: ExternalMcpConnection[] };
  return (record.connections ?? []).map((connection) => ({
    ...connection,
    requiredBy: parseRequiredBy(connection.requiredBy),
    identityManagedBy: parseRequiredBy(connection.identityManagedBy),
    updatedAt: typeof connection.updatedAt === "string" ? connection.updatedAt : null,
    ...(typeof connection.createdByName === "string" || connection.createdByName === null ? { createdByName: connection.createdByName } : {}),
    ...(typeof connection.needsReconnect === "boolean" ? { needsReconnect: connection.needsReconnect } : {}),
    ...(connection.credentialHealth === "unknown" || connection.credentialHealth === "ready" || connection.credentialHealth === "reconnect_required"
      ? { credentialHealth: connection.credentialHealth }
      : {}),
    ...(typeof connection.issuerReviewRequired === "boolean" ? { issuerReviewRequired: connection.issuerReviewRequired } : {}),
    ...(connection.reconnectActionOwner === "member" || connection.reconnectActionOwner === "organization_admin" || connection.reconnectActionOwner === null
      ? { reconnectActionOwner: connection.reconnectActionOwner }
      : {}),
    ...(isStringArray(connection.missingFeatures) ? { missingFeatures: connection.missingFeatures } : {}),
    ...(typeof connection.externalAccountId === "string" || connection.externalAccountId === null

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. For 401/403, re-authenticate and verify getOrgScopeHeaders receives a valid orgId.
  2. For 400, check the scope value against the API version the server runs (client/server mismatch after upgrade).
  3. For 5xx, retry (TanStack Query retry) with backoff and check Den server health.
  4. In the component, branch on error state and call isReauthRequiredError to trigger re-auth instead of rendering a blank list.

Example fix

// before
const { data: connections } = useMcpConnections(orgId, scope);
// after
const { data: connections, error, refetch } = useMcpConnections(orgId, scope);
if (error) {
  if (isReauthRequiredError(error)) startReauth();
  else if (isRetryable(error)) refetch();
  else showError(error.message);
}
Defensive patterns

Strategy: retry

Validate before calling

// before querying
if (!orgId) throw new Error("Select an organization to list MCP connectors");
const validScopes = ["org", "user"];
if (!validScopes.includes(scope)) throw new Error(`Unsupported scope: ${scope}`);

Type guard

function isRetryableListError(err: unknown): boolean {
  return err instanceof Error && /\((5\d\d|408|429)\)/.test(err.message);
}

Try / catch

useQuery({
  queryKey: mcpConnectionQueryKeys.list(orgId, scope),
  queryFn: () => fetchConnections(orgId, scope),
  retry: (count, err) => count < 3 && isRetryableListError(err),
});

Prevention

When it happens

Trigger: Non-ok response listing org MCP connections: 401 when the Den session expired, 403 when the org scope headers are missing/invalid or the user lacks read access, 400 for an unsupported scope value, 5xx on server faults or during Den deploys.

Common situations: Opening the MCP connectors page after a long idle session (expired token); switching orgs with a stale orgId header; a bad scope query parameter after a client/server version mismatch; transient Den server 5xx.

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/00796f3f4f46c15c. Report an issue: GitHub.