windmill-labs/windmill · error · Error

Job ID not set

Error message

Job ID not set

What it means

getRootJobId(jobId?) resolves the root (flow-parent) job id for the current run via JobService.getRootJobId. The jobId defaults to the WM_JOB_ID environment variable; if neither the argument nor that env var is available, it throws 'Job ID not set'. This typically means the code ran outside a Windmill job execution context (where WM_JOB_ID is injected).

Source

Thrown at typescript-client/client.ts:154

      return undefined;
    } else {
      throw Error(
        `Resource not found at ${path} or not visible to you: ${e.body}`
      );
    }
  }
}

/**
 * Get the true root job id
 * @param jobId job id to get the root job id from (default to current job)
 * @returns root job id
 */
export async function getRootJobId(jobId?: string): Promise<string> {
  const workspace = getWorkspace();
  jobId = jobId ?? getEnv("WM_JOB_ID");
  if (jobId === undefined) {
    throw Error("Job ID not set");
  }
  return await JobService.getRootJobId({ workspace, id: jobId });
}

/**
 * @deprecated Use runScriptByPath or runScriptByHash instead
 */
export async function runScript(
  path: string | null = null,
  hash_: string | null = null,
  args: Record<string, any> | null = null,
  verbose: boolean = false,
  tag: string | null = null
): Promise<any> {
  console.warn(
    "runScript is deprecated. Use runScriptByPath or runScriptByHash instead."
  );
  if (path && hash_) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Run the code inside a Windmill job (script/flow step) so WM_JOB_ID is injected.
  2. Pass the job id explicitly: getRootJobId('some-job-id') when calling outside a run.
  3. In local dev, set WM_JOB_ID in the environment to a real job id copied from the runs page.
  4. Guard flow-state helpers so they no-op outside Windmill: if (!getEnv('WM_JOB_ID')) return.

Example fix

// before
const rootId = await getRootJobId() // throws locally
// after
const rootId = process.env.WM_JOB_ID ? await getRootJobId() : undefined
Defensive patterns

Strategy: validation

Validate before calling

import { getEnv } from 'windmill-client'
if (!jobId && !getEnv('WM_JOB_ID')) {
  console.warn('Not running inside a Windmill job; skipping flow state access')
  return
}
await getRootJobId(jobId)

Type guard

function hasJobContext(jobId?: string): boolean {
  return Boolean(jobId ?? process.env.WM_JOB_ID)
}

Try / catch

try {
  await setFlowUserState(key, value)
} catch (e) {
  if (e?.message === 'Job ID not set') {
    console.warn('Flow state unavailable outside Windmill jobs')
  } else throw e
}

Prevention

When it happens

Trigger: Calling getRootJobId() (or setFlowUserState/getFlowUserState, which call it) from code running outside a Windmill worker — local dev, plain Node, tests — or inside a job type that does not set WM_JOB_ID (e.g. some hooks/preview contexts).

Common situations: Running a script locally with `node`/`tsx` to debug flow user state code; unit tests importing the helper; code shared between Windmill jobs and external tooling executing outside the runner; a container image or executor that drops the WM_JOB_ID env var.

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