windmill-labs/windmill · error · Error

Error setting flow user state at ${key}: ${e.body}

Error message

Error setting flow user state at ${key}: ${e.body}

What it means

setFlowUserState() wraps JobService.setFlowUserState, which writes a key/value into the user state of the root flow job. Any API failure (non-flow context, job not found, permissions, network) is caught and, only if errorIfNotPossible is true, rethrown with this message including the API error body; otherwise it is just logged. The original error object is discarded, so only e.body is visible.

Source

Thrown at typescript-client/client.ts:719

export async function setFlowUserState(
  key: string,
  value: any,
  errorIfNotPossible?: boolean
): Promise<void> {
  if (value === undefined) {
    value = null;
  }
  const workspace = getWorkspace();
  try {
    await JobService.setFlowUserState({
      workspace,
      id: await getRootJobId(),
      key,
      requestBody: value,
    });
  } catch (e: any) {
    if (errorIfNotPossible) {
      throw Error(`Error setting flow user state at ${key}: ${e.body}`);
    } else {
      console.error(`Error setting flow user state at ${key}: ${e.body}`);
    }
  }
}

/**
 * Get a flow user state
 * @param path path of the variable

 */
export async function getFlowUserState(
  key: string,
  errorIfNotPossible?: boolean
): Promise<any> {
  const workspace = getWorkspace();
  try {
    return await JobService.getFlowUserState({

View on GitHub (pinned to e474e8803c)

Solutions

  1. Only call setFlowUserState from within a flow execution, or pass errorIfNotPossible=false to tolerate the failure.
  2. Verify the flow job is still running when the call happens (state can't be set after completion).
  3. Read the appended e.body in the message — it carries the server's actual reason (404/403/validation).
  4. Check workspace and token permissions for JobService.setFlowUserState on the root job.
  5. If thrown persistently due to network, add retry logic around the call.

Example fix

// before (throws and aborts the step)
await setFlowUserState('progress', 42, true);
// after
try {
  await setFlowUserState('progress', 42, true);
} catch (e) {
  console.error('flow user state unavailable, continuing', e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const rootJobId = getEnv('WM_FLOW_JOB_ID');
if (!rootJobId) {
  console.warn('Not in a flow context; setFlowUserState will fail');
}

Try / catch

try {
  await setFlowUserState(key, value, true);
} catch (e) {
  // e.message embeds the API e.body; decide whether the step can proceed without state
  console.error(`Could not set flow user state '${key}':`, e.message);
}

Prevention

When it happens

Trigger: Calling setFlowUserState(key, value, true) when the code is not running inside a flow (getRootJobId has no root job), the flow job already finished/doesn't exist, the key/value is rejected by the API, or a transient network/auth failure occurs.

Common situations: Using flow user state in a standalone script (not a flow step); writing state after the flow run completed or was cancelled; worker connectivity problems; calling it from tests with a mocked/absent job id.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/f9ed6c291576250c. Report an issue: GitHub.