different-ai/openwork · error

Delete MCP connection response was incomplete.

Error message

Delete MCP connection response was incomplete.

What it means

The delete (remove) MCP connection mutation assigns `result = connectionId` in its runReauthableAction callback after a 2xx remove response. This guard throws when that assignment never happened, protecting callers from a mutation resolving with an undefined id while onSuccess cache invalidation assumes a completed delete.

Source

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

export function useDeleteMcpConnection() {
  const queryClient = useQueryClient();
  const { orgId, runReauthableAction } = useOrgDashboard();

  return useMutation({
    mutationFn: async (connectionId: string): Promise<string> => {
      let result: string | null = null;
      await runReauthableAction("delete-mcp-connection", async () => {
        const { response, payload } = await requestJson(
          `/v1/mcp-connections/${encodeURIComponent(connectionId)}`,
          { method: "DELETE", headers: getOrgScopeHeaders(requireOrgId(orgId)) },
          15000,
        );
        if (!response.ok) {
          throw getRequestError(payload, response, `Failed to remove MCP connection (${response.status}).`);
        }
        result = connectionId;
      });
      if (!result) throw new Error("Delete MCP connection response was incomplete.");
      return result;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: mcpConnectionQueryKeys.all });
    },
  });
}

export type SaveNativeProviderClientInput = {
  providerId: string;
  clientId?: string;
  clientSecret?: string;
  tenantId?: string;
  features: string[];
};

export type NativeProviderClient = {
  providerId: string;

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Guard the UI so delete can't be invoked without a valid connectionId
  2. Confirm runReauthableAction always runs the callback exactly once and throws on auth failure
  3. Add logging in the callback to verify the remove fetch succeeded and assignment ran
  4. Check onSuccess invalidation still runs — on failure the list query may show a stale row; invalidate manually in the catch path

Example fix

// before
if (!result) throw new Error("Delete MCP connection response was incomplete.");
// after
if (!result) {
  throw new Error(`Delete MCP connection: no connectionId assigned (input=${JSON.stringify(connectionId)}).`);
}
Defensive patterns

Strategy: validation

Validate before calling

if (typeof connectionId !== 'string' || connectionId.length === 0) {
  throw new Error('cannot delete: connectionId missing');
}

Type guard

function isDeleteInput(v: unknown): v is { connectionId: string } {
  return isRecord(v) && typeof v.connectionId === 'string' && v.connectionId.length > 0;
}

Try / catch

try {
  await removeConnection.mutateAsync(connectionId);
} catch (e) {
  showToast({ variant: 'error', title: 'Delete failed', description: e instanceof Error ? e.message : String(e) });
  queryClient.invalidateQueries({ queryKey: mcpConnectionQueryKeys.all });
}

Prevention

When it happens

Trigger: mutateAsync delete with falsy connectionId, or runReauthableAction resolving without re-executing the callback after a dismissed or failed re-auth.

Common situations: Double-delete race: first click removed the row and cleared its id; session re-auth interrupted the flow; wrapper refactor changed semantics.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


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