different-ai/openwork · error

Update connection access response was incomplete.

Error message

Update connection access response was incomplete.

What it means

The update-connection-access mutation assigns `result = input.connectionId` inside the runReauthableAction callback after a successful fetch. The connectionId is a local value, so `result` can only be falsy if the callback never ran to completion (e.g. re-auth wrapper resolved without executing) or connectionId was empty/undefined in the input. The guard surfaces that as a thrown error rather than silently resolving the mutation.

Source

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

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

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

export function useStartMcpConnectionOAuth() {
  const { orgId } = useOrgDashboard();

  return useMutation({
    mutationFn: async (connectionId: string): Promise<{ status: "connected" | "needs_auth"; authorizeUrl: string | null }> => {
      const { response, payload } = await requestJson(
        `/v1/mcp-connections/${encodeURIComponent(connectionId)}/connect/start`,
        { headers: getOrgScopeHeaders(requireOrgId(orgId)) },
        20000,
      );

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Validate input.connectionId is a non-empty string before calling mutateAsync
  2. Confirm runReauthableAction re-executes its callback after successful re-auth and throws (not silently resolves) on dismissal
  3. Log the callback execution to see whether the fetch+assignment path ran
  4. Check that the access-toggle endpoint's 2xx response isn't short-circuiting the assignment

Example fix

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

Strategy: validation

Validate before calling

function assertConnectionAccessInput(input: { connectionId: string; access: 'private' | 'shared' }) {
  if (!input.connectionId) throw new Error('connectionId is required before updating access');
  if (!['private', 'shared'].includes(input.access)) throw new Error(`invalid access: ${input.access}`);
}

Type guard

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

Try / catch

try {
  await updateAccess.mutateAsync({ connectionId, access });
} catch (e) {
  showToast({ variant: 'error', title: 'Could not change access', description: e instanceof Error ? e.message : String(e) });
  queryClient.invalidateQueries({ queryKey: mcpConnectionQueryKeys.all });
}

Prevention

When it happens

Trigger: mutateAsync on update connection access with input.connectionId undefined/empty string, or runReauthableAction resolving without re-running the callback after an auth prompt was dismissed.

Common situations: UI passed a stale/missing connectionId after a list refetch removed the row; expired Den session triggered re-auth that was cancelled; wrapper contract change after refactor.

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/98db2fa4a0d51595. Report an issue: GitHub.