different-ai/openwork · error

Workflow action failed

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.

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.

Appendix: 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)