windmill-labs/windmill · error · Exception

Result was none

Error message

Result was none

What it means

wait_job polls a job until completion; when the job succeeds but its result is null and `assert_result_is_not_none=True`, the client raises 'Result was none'. It guards callers that require a meaningful return value from the script/flow.

Source

Thrown at python-client/wmill/wmill/client.py:430

            result_res = self.get(
                f"/w/{self.workspace}/jobs_u/completed/get_result_maybe/{job_id}", True
            ).json()

            started = result_res["started"]
            completed = result_res["completed"]
            success = result_res["success"]

            if not started and verbose:
                logger.info(f"job {job_id} has not started yet")

            if cleanup and completed:
                atexit.unregister(cancel_job)

            if completed:
                result = result_res["result"]
                if success:
                    if result is None and assert_result_is_not_none:
                        raise Exception("Result was none")
                    return result
                else:
                    error = result["error"]
                    raise Exception(f"Job {job_id} was not successful: {str(error)}")

            if timeout and ((time.time() - start_time) > timeout):
                msg = "reached timeout"
                logger.warning(msg)
                self.post(
                    f"/w/{self.workspace}/jobs_u/queue/cancel/{job_id}",
                    json={"reason": msg},
                )
                raise TimeoutError(msg)
            if verbose:
                logger.info(f"sleeping 0.5 seconds for {job_id = }")

            time.sleep(0.5)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Add a return value to the script (Python: `return {...}` as the last statement; TypeScript: return an object).
  2. Set assert_result_is_not_none=False if a None result is acceptable.
  3. Check the job's result in the UI/runs page to confirm what the script actually returned.

Example fix

// before (script)
def main():
    do_work()  # no return -> result None
// after (script)
def main():
    result = do_work()
    return result
// caller
result = client.wait_job(job_id, assert_result_is_not_none=True)
Defensive patterns

Strategy: try-catch

Validate before calling

# before asserting, inspect the job result
completed = client.get_job(job_id)
if completed.get('success') and completed.get('result') is None:
    logger.warning('job %s returned None; fix the script to return a value', job_id)

Type guard

def has_result(job_result) -> bool:
    return job_result is not None

Try / catch

try:
    result = client.wait_job(job_id, assert_result_is_not_none=True)
except Exception as e:
    if str(e) == 'Result was none':
        result = default_value_or_retry()
    else:
        raise

Prevention

When it happens

Trigger: Calling wait_job(job_id, assert_result_is_not_none=True) on a job whose script finished successfully but returned None (script has no return/last expression, or explicitly returns None).

Common situations: Python script missing a final `return` statement so the job result is null; script intentionally side-effect-only but caller still asserts non-None; flow whose last step produces no output.

Related errors


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