{"record":{"id":"5a0d29275849cea3","repo":"windmill-labs/windmill","slug":"wait-for-approval-can-only-be-called-inside-a-wor","errorCode":null,"errorMessage":"wait_for_approval can only be called inside a @workflow","messagePattern":"wait_for_approval can only be called inside a @workflow","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python-client/wmill/wmill/client.py","lineNumber":3459,"sourceCode":"\n    Args:\n        timeout: Approval timeout in seconds (default 1800).\n        form: Optional form schema for the approval page.\n        self_approval: Whether the user who triggered the flow can approve it (default True).\n        key: Optional checkpoint key naming this approval step.\n\n    Example::\n\n        urls = await step(\"urls\", lambda: get_approval_urls(\"manager\"))\n        await step(\"notify\", lambda: send_email(urls[\"resume\"], urls[\"cancel\"]))\n        result = await wait_for_approval(key=\"manager\", timeout=3600)\n    \"\"\"\n    ctx: WorkflowCtx | None = _workflow_ctx.get(None)\n    if ctx is not None:\n        return await ctx._wait_for_approval(\n            timeout=timeout, form=form, self_approval=self_approval, key=key\n        )\n    raise RuntimeError(\"wait_for_approval can only be called inside a @workflow\")\n\n\nasync def parallel(items, fn, *, concurrency: Optional[int] = None):\n    \"\"\"Process items in parallel with optional concurrency control.\n\n    Each item is processed by calling ``fn(item)``, which should be a @task.\n    Items are dispatched in batches of ``concurrency`` (default: all at once).\n\n    Example::\n\n        @task\n        async def process(item: str):\n            ...\n\n        results = await parallel(items, process, concurrency=5)\n    \"\"\"\n    if not items:\n        return []","sourceCodeStart":3441,"sourceCodeEnd":3477,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/python-client/wmill/wmill/client.py#L3441-L3477","documentation":"wait_for_approval pauses a workflow until a human approves via a generated form. The pause/resume mechanism is implemented by the workflow context (_workflow_ctx), so calling wait_for_approval outside a @workflow cannot suspend anything and the library raises this RuntimeError instead of hanging or silently continuing.","triggerScenarios":"Calling wmill.wait_for_approval(...) at the top level of a plain script, inside a non-flow job, or in a context where _workflow_ctx.get() is None (e.g. approval logic factored out of the flow into a helper executed outside it).","commonSituations":"Testing approval logic locally with `python script.py` instead of running the flow; reusing an approval helper in a scheduled standalone script; calling wait_for_approval from an inline script step that is not part of a flow.","solutions":["Run the code as part of a flow step (@wmill.workflow) so the approval can suspend/resume the run","Replace wait_for_approval outside flows with the approval REST API (create a suspended run / use wmill CLI approval commands) if you must orchestrate manually","Structure the flow so approval stays inside the flow definition rather than in a shared helper called from a plain script"],"exampleFix":"// before\nwait_for_approval(form=approvers_form)  # plain script -> RuntimeError\n// after\nimport wmill\n@wmill.workflow\ndef order_flow():\n    wmill.wait_for_approval(form=approvers_form)\n    return \"approved\"","handlingStrategy":"validation","validationCode":"from wmill.client import _workflow_ctx\nif _workflow_ctx.get(None) is None:\n    raise RuntimeError(\"wait_for_approval requires a @workflow (flow) context\")","typeGuard":"def in_workflow() -> bool:\n    from wmill.client import _workflow_ctx\n    return _workflow_ctx.get(None) is not None","tryCatchPattern":"try:\n    wmill.wait_for_approval(form=f)\nexcept RuntimeError as e:\n    if \"can only be called inside a @workflow\" in str(e):\n        handle_outside_flow_approval()  # e.g. use approval REST API\n    else:\n        raise","preventionTips":["Place wait_for_approval directly in flow steps, not in helpers reused by plain scripts","Never call it from scheduled standalone scripts","Test approval flows by running the actual flow, not by invoking the helper locally"],"tags":["python","windmill","approval","workflow-context"],"backgroundTag":"workflow-context-required","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}