different-ai/openwork · error

Workflow action failed (${response.status}).

Error message

Workflow action failed (${response.status}).

What it means

mutationJson performs POST/PUT workflow mutations (activate/retire artifact view) via requestJson and throws 'Workflow action failed (STATUS).' when response.ok is false, preferring a server-provided error message from getErrorMessage(payload, fallback). It is the single choke point for all workflow mutation calls in this module.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/workflow-detail-data.tsx:53

    throw new Error("Workflow response was incomplete.");
  }
  return {
    workflow: {
      type: "workflow", id: workflow.id, plugin, name: workflow.name,
      description: typeof workflow.description === "string" ? workflow.description : null,
      role, state, resultState,
      latestSuccessfulAt: typeof workflow.latestSuccessfulAt === "string" ? workflow.latestSuccessfulAt : null,
      viewState, activeViewTitle: typeof workflow.activeViewTitle === "string" ? workflow.activeViewTitle : null,
      automationCount: workflow.automationCount, source: { kind: sourceKind },
    },
    script: workflowDetailSchema.parse(value.script),
    views: value.views.map((view) => generatedArtifactViewSchema.parse(view)),
  };
}

async function mutationJson(path: string, method: "POST" | "PUT") {
  const { response, payload } = await requestJson(path, { method }, 15_000);
  if (!response.ok) throw new Error(getErrorMessage(payload, `Workflow action failed (${response.status}).`));
  return payload;
}

export function useWorkflowLibraryDetail(workflowId: string) {
  return useQuery({
    queryKey: ["workflow", workflowId],
    queryFn: async () => {
      const { response, payload } = await requestJson(`/v1/workflows/${encodeURIComponent(workflowId)}`, { method: "GET" }, 15_000);
      if (!response.ok) throw new Error(getErrorMessage(payload, `Failed to load Workflow (${response.status}).`));
      return parseWorkflowDetail(payload);
    },
  });
}

export function useActivateArtifactView(workflowId: string) {
  const client = useQueryClient();
  return useMutation({
    mutationFn: async ({ viewId, revisionId }: { viewId: string; revisionId: string }) => generatedArtifactViewSchema.parse(await mutationJson(

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Read the response.status and any message in the JSON payload to identify the exact server rejection cause.
  2. Re-authenticate if status is 401 (sign-in flow refreshes the Den session).
  3. Verify the current member's role permits activating/retiring views (owner/admin).
  4. Confirm the workflow and view ids still exist; refresh the detail query.
  5. Retry after backing off if status >= 500 or the request timed out.

Example fix

// before
const { response, payload } = await requestJson(path, { method }, 15_000);
if (!response.ok) throw new Error(getErrorMessage(payload, `Workflow action failed (${response.status}).`));
// after (caller-side retry on transient failures)
try { await mutationJson(path, "POST"); }
catch (e) { if (isTransient(e)) await retryWithBackoff(() => mutationJson(path, "POST")); else throw e; }
Defensive patterns

Strategy: try-catch

Validate before calling

if (!workflowId || !viewId) throw new Error("Missing workflow/view id before mutation");

Type guard

null

Try / catch

try {
  await mutationJson(`/v1/workflows/${id}/views/${viewId}`, "POST");
} catch (e) {
  if (e instanceof Error && e.message.startsWith("Workflow action failed")) {
    if (/\(401\)/.test(e.message)) await reauth();
    else if (/\((5\d\d)\)/.test(e.message)) scheduleRetry();
    else showToast(e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Any POST/PUT to a workflow view endpoint returning a non-2xx: 401 expired session, 403 insufficient role, 404 unknown workflow/view id, 409 conflicting view state, 422 invalid view payload, 5xx Den server error, or a timeout at the 15s requestJson limit.

Common situations: Session token expired mid-edit, user role downgraded below what the mutation requires, activating a view on a workflow that was concurrently retired, self-hosted Den behind a misconfigured proxy returning 502, or the 15s timeout firing on a slow network.

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/87d4542af735d11e. Report an issue: GitHub.