windmill-labs/windmill · error · Error

State path not set

Error message

State path not set

What it means

This library resolves the persistent state resource path from the environment variables WM_STATE_PATH_NEW or WM_STATE_PATH. getStatePath() throws this error when neither variable is set, because state functions (setState/getState and their deprecated counterparts) need a resource path to read/write state. It only makes sense inside a Windmill script/flow execution, where the runner injects these variables.

Source

Thrown at typescript-client/client.ts:590

 * @param obj resource value or path of the resource under the format `$res:path`
 * @returns resource value
 */
export async function resolveDefaultResource(obj: any): Promise<any> {
  if (typeof obj === "string" && obj.startsWith("$res:")) {
    return await getResource(obj.substring(5), true);
  } else {
    return obj;
  }
}

/**
 * Get the state file path from environment variables
 * @returns State path string
 */
export function getStatePath(): string {
  const state_path = getEnv("WM_STATE_PATH_NEW") ?? getEnv("WM_STATE_PATH");
  if (state_path === undefined) {
    throw Error("State path not set");
  }
  return state_path;
}

/**
 * Set a resource value by path
 * @param path path of the resource to set, default to state path
 * @param value new value of the resource to set
 * @param initializeToTypeIfNotExist if the resource does not exist, initialize it with this type
 */
export async function setResource(
  value: any,
  path?: string,
  initializeToTypeIfNotExist?: string
): Promise<void> {
  path = parseResourceSyntax(path) ?? path ?? getStatePath();
  const mockedApi = await getMockedApi();
  if (mockedApi) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Run the code inside a Windmill execution (worker) so WM_STATE_PATH / WM_STATE_PATH_NEW are injected, instead of locally.
  2. For local runs, export a dummy path: `export WM_STATE_PATH=u/admin/my_state` pointing at an existing state resource.
  3. If running on an old worker image, upgrade the worker so it sets WM_STATE_PATH_NEW.
  4. Pass an explicit `path` argument to setState(state, path)/getState(path) to bypass getStatePath().

Example fix

// before (fails locally)
await setState({ count: 1 });
// after
if (!process.env.WM_STATE_PATH && !process.env.WM_STATE_PATH_NEW) {
  process.env.WM_STATE_PATH = 'u/admin/my_state'; // or skip setState locally
}
await setState({ count: 1 });
Defensive patterns

Strategy: validation

Validate before calling

function canUseState(): boolean {
  return process.env.WM_STATE_PATH_NEW !== undefined || process.env.WM_STATE_PATH !== undefined;
}
if (!canUseState()) {
  console.warn('State unavailable outside Windmill; skipping state persistence');
}

Type guard

function hasStatePath(env: NodeJS.ProcessEnv = process.env): env is NodeJS.ProcessEnv & { WM_STATE_PATH: string } {
  return env.WM_STATE_PATH_NEW !== undefined || env.WM_STATE_PATH !== undefined;
}

Try / catch

let state;
try {
  state = await getState();
} catch {
  state = {}; // local/default state when WM_STATE_PATH is absent
}

Prevention

When it happens

Trigger: Calling setState(state), getState(), setInternalState() or getInternalState() (via getStatePath) outside a Windmill worker, e.g. in local Node tests, CI, or a plain node script, or running on an old/patched runner that no longer injects WM_STATE_PATH.

Common situations: Running a script locally with `npm run dev` or a unit test instead of via `wmill script execute` or the Windmill UI; using an outdated worker image predating WM_STATE_PATH_NEW; executing the compiled client standalone after moving code out of a Windmill step.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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