windmill-labs/windmill · error · Error

path and hash_ are mutually exclusive

Error message

path and hash_ are mutually exclusive

What it means

runScriptAsync accepts either a script path or a deployed script hash to identify the script to run, but not both. Passing both arguments throws this error immediately, before any API call, because the two identifiers are alternative selectors for the same script.

Source

Thrown at backend/windmill-runtime-nativets/src/windmill-client.js:9975

        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${getEnv("WM_TOKEN")}`,
        },
        body: JSON.stringify({ args }),
      }
    );
    let jobId = await req.text();
    console.log(`Started task ${f.name} as job ${jobId}`);
    let r = await waitJob(jobId);
    console.log(`Task ${f.name} (${jobId}) completed`);
    return r;
  };
}
async function runScriptAsync(path, hash_, args, scheduledInSeconds = null) {
  !clientSet && setClient();
  if (path && hash_) {
    throw new Error("path and hash_ are mutually exclusive");
  }
  args = args || {};
  const params = {};
  if (scheduledInSeconds) {
    params["scheduled_in_secs"] = scheduledInSeconds;
  }
  let parentJobId = getEnv("WM_JOB_ID");
  if (parentJobId !== void 0) {
    params["parent_job"] = parentJobId;
  }
  let rootJobId = getEnv("WM_ROOT_FLOW_JOB_ID");
  if (rootJobId != void 0 && rootJobId != "") {
    params["root_job"] = rootJobId;
  }
  let endpoint;
  if (path) {
    endpoint = `/w/${getWorkspace()}/jobs/run/p/${path}`;
  } else if (hash_) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Pass only one identifier: set the other argument to null/undefined.
  2. Prefer the path for readability, or the hash to pin an exact deployed version.
  3. Add a validation/coalescing step upstream so only one of the two is ever forwarded.

Example fix

// before
await runScriptAsync(cfg.path, cfg.hash, args); // throws when both set
// after
await runScriptAsync(cfg.path ?? null, cfg.hash ?? null, args); // still validate one is set
// or explicitly:
await runScriptAsync(cfg.hash ? null : cfg.path, cfg.hash, args);
Defensive patterns

Strategy: validation

Validate before calling

function runScriptChecked(path, hash_, args) {
  if (path && hash_) throw new TypeError('path and hash_ are mutually exclusive');
  if (!path && !hash_) throw new TypeError('path or hash_ must be provided');
  return runScriptAsync(path ?? null, hash_ ?? null, args);
}

Prevention

When it happens

Trigger: Calling runScriptAsync(path, hash_, args) with truthy values for both `path` and `hash_` — e.g. copying an example that sets both, or forwarding optional parameters without nulling the unused one.

Common situations: Dynamic code that computes both a path and a hash from config and doesn't clear the other; wrappers around runScriptAsync that pass through raw config objects.

Related errors


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