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 === nullView on GitHub (pinned to 2b7df46e8a)
Solutions
- For 401/403, re-authenticate and verify getOrgScopeHeaders receives a valid orgId.
- For 400, check the scope value against the API version the server runs (client/server mismatch after upgrade).
- For 5xx, retry (TanStack Query retry) with backoff and check Den server health.
- 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
- Keep the org context synchronized with the session to avoid stale scope headers
- Proactively refresh sessions on 401 instead of failing the list render
- Pin client and server API versions so the scope parameter stays valid
- Render an explicit error state with a retry button from the query error
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
- Failed to configure plugin connection (${response.status}).
- Failed to inspect MCP tools (${response.status}).
- Failed to update MCP tool policy (${response.status}).
- Failed to load MCP presets (${response.status}).
- Failed to look up the MCP server (${response.status}).
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/00796f3f4f46c15c.
Report an issue: GitHub.