windmill-labs/windmill · error · Error

Job ${jobId} was not successful: ${JSON.stringify(error)}

Error message

Job ${jobId} was not successful: ${JSON.stringify(error)}

What it means

When waiting on a Windmill job by id, the poller sees the job completed but its result carries an error field instead of a successful result. The client throws an Error embedding the jobId and a JSON dump of the error object so the script's own failure is visible to the caller of runScript/waitJob.

Source

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

  const jobId = await runScriptAsync(path, hash_, args);
  return await waitJob(jobId, verbose);
}
async function waitJob(jobId, verbose = false) {
  while (true) {
    const resultRes = await getResultMaybe(jobId);
    const started = resultRes.started;
    const completed = resultRes.completed;
    const success = resultRes.success;
    if (!started && verbose) {
      console.info(`job ${jobId} has not started yet`);
    }
    if (completed) {
      const result = resultRes.result;
      if (success) {
        return result;
      } else {
        const error = result.error;
        throw new Error(
          `Job ${jobId} was not successful: ${JSON.stringify(error)}`
        );
      }
    }
    if (verbose) {
      console.info(`sleeping 0.5 seconds for jobId: ${jobId}`);
    }
    await new Promise((resolve2) => setTimeout(resolve2, 500));
  }
}
async function getResultMaybe(jobId) {
  !clientSet && setClient();
  const workspace = getWorkspace();
  return await JobService.getCompletedJobResultMaybe({ workspace, id: jobId });
}
var STRIP_COMMENTS =
  /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/gm;
var ARGUMENT_NAMES = /([^\s,]+)/g;

View on GitHub (pinned to e474e8803c)

Solutions

  1. Inspect the JSON error payload in the message — it names the root cause (script exception, worker crash, denied, timeout).
  2. Open the job's run page in the Windmill UI / GET /jobs/u/get/{jobId} for the full logs and stack trace.
  3. Fix the underlying script bug or pass correct args; re-run.
  4. If the error is transient (worker OOM, timeout), increase limits or retry with backoff.

Example fix

// before
const result = await windmill.runScript(path, hash_, args); // throws on job error
// after
try {
  const result = await windmill.runScript(path, hash_, args);
} catch (e) {
  const detail = /Job \S+ was not successful: (.*)/.exec(e.message);
  if (detail) console.error('Job failed:', JSON.parse(detail[1]));
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate args against the script's signature before running
const sig = await client.getScriptArgsByPath({ workspace, path });
for (const req of sig.args.filter(a => a.has_default === false)) {
  if (!(req.name in args)) throw new Error(`Missing arg ${req.name} for ${path}`);
}

Type guard

function isJobFailure(e) { return /Job \S+ was not successful:/.test(e?.message ?? ''); }
function parseJobError(e) { const m = /Job \S+ was not successful: (.*)$/.exec(e.message); return m ? JSON.parse(m[1]) : null; }

Try / catch

try {
  const result = await windmill.runScript(path, hash_, args);
} catch (e) {
  if (isJobFailure(e)) {
    const jobError = parseJobError(e);
    console.error('Underlying job error:', jobError);
    // fetch full logs via GET /jobs/u/get/logs/{jobId}
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling runScript/runScriptAsync + waiting for the resulting job when the executed script throws, is denied (job error: permissions, rate limit), or fails at runtime; result.error is populated on completion and this branch fires instead of returning the result.

Common situations: The child script itself raised an exception, input args didn't match the script signature, the script exceeded its timeout or memory limit, or the worker crashed the job.

Related errors


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