different-ai/openwork · error · Error

Automation cancellation response was invalid.

Error message

Automation cancellation response was invalid.

What it means

useCancelAutomationRun POSTs /v1/automation-runs/{runId}/cancel and expects a 2xx body containing the updated run. If the body is not an object or lacks the run field, this error is thrown. The mutation uses useAutomationMutation, so the error surfaces in the mutation's error state rather than crashing.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/automation-data.tsx:127

      { method: "POST" },
    )),
  );
}

export function useArchiveAutomation() {
  return useAutomationMutation<string, ReturnType<typeof automationDetailSchema.parse>>(
    async (automationId) => automationDetailSchema.parse(await payload(
      `/v1/automations/${encodeURIComponent(automationId)}`,
      { method: "DELETE" },
    )),
  );
}

export function useCancelAutomationRun() {
  return useAutomationMutation<string, ReturnType<typeof automationRunSchema.parse>>(
    async (runId) => {
      const value = await payload(`/v1/automation-runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
      if (typeof value !== "object" || value === null || !("run" in value)) throw new Error("Automation cancellation response was invalid.");
      return automationRunSchema.parse(value.run);
    },
  );
}

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Inspect the cancel response body and compare with the expected {run:{...}} shape.
  2. Handle already-completed runs server-side by returning the run in a terminal state instead of an empty body.
  3. Update client unwrapping if the server now uses an envelope.
  4. Retry: if the run already finished, the cancel is a no-op — refresh runs via query invalidation instead.

Example fix

// before
if (typeof value !== "object" || value === null || !("run" in value)) throw new Error("Automation cancellation response was invalid.");
// after
const run = (value as { run?: unknown })?.run;
if (run === undefined && (value as { status?: string })?.status === "already_finished") {
  return null; // treat as no-op
}
if (run === undefined) throw new Error("Automation cancellation response was invalid.");
Defensive patterns

Strategy: type-guard

Validate before calling

const value = await payload(`/v1/automation-runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
if (!hasRun(value)) { /* treat as inconclusive; refetch run state */ }

Type guard

function hasRun(v: unknown): v is { run: unknown } {
  return typeof v === "object" && v !== null && "run" in v;
}

Try / catch

const cancelRun = useCancelAutomationRun();
cancelRun.mutate(runId, {
  onError: (err) => {
    if (err instanceof Error && err.message.includes("cancellation response was invalid")) {
      // inconclusive: poll run status instead of assuming failure
      refetchRun(runId);
    }
  },
});

Prevention

When it happens

Trigger: Cancel endpoint returns 200/202 with an empty body or only {status:"cancelling"}; envelope reshaped by the server or a proxy; run already finished so the server returns a success body without the run object.

Common situations: Server/client version mismatch; run completed before the cancel arrived and the handler short-circuits with an empty 200; intermediate proxy stripping the body on 202.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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