windmill-labs/windmill · error · RuntimeError
WAC step key "{requested_key}" is already used in this workf
Error message
WAC step key "{requested_key}" is already used in this workflow. Give each wait_for_approval() its own key so get_approval_urls() can address it. What it means
In WAC (workflow-as-code) runs each wait_for_approval must have a unique explicit key because get_approval_urls mints URLs addressed by that key. On a duplicate explicit key the library refuses to silently rename it to <key>_2 — that would hand the caller a URL for the first step, which then fails with 'resume request already sent' and parks the workflow until timeout — and raises this RuntimeError instead.
Source
Thrown at python-client/wmill/wmill/client.py:2996
)
async def _wait_for_approval(
self,
timeout: int = 1800,
form: dict | None = None,
self_approval: bool = True,
key: str | None = None,
):
if key is not None:
_assert_usable_step_key(key, "wait_for_approval key")
requested_key, key = key, self._alloc_key(key or "approval")
# An explicit key is an identifier callers mint URLs against, so silently
# renaming a duplicate to ``<key>_2`` would hand them a URL for the *first*
# step — which then fails with "resume request already sent" and parks the
# workflow until timeout. Unnamed approvals keep auto-numbering.
if requested_key and key != requested_key:
raise RuntimeError(
f'WAC step key "{requested_key}" is already used in this workflow. '
"Give each wait_for_approval() its own key so get_approval_urls() can address it."
)
if key in self._completed:
return self._completed[key]
if self._executing_key is not None:
await _asyncio.Future()
print(f"\n--- WAC: wait_for_approval({key}) ---")
raise _StepSuspend({
"mode": "approval",
"key": key,
"timeout": timeout,
"form": form,
"self_approval_disabled": not self_approval,
"steps": [],View on GitHub (pinned to e474e8803c)
Solutions
- Give each call a unique explicit key, e.g. f"approval-{i}" in loops
- Omit the key on later approvals to get automatic numbering
- Search the workflow for duplicated key= arguments to wait_for_approval
- Thread a unique suffix parameter through shared approval helpers
Example fix
// before
for i, approver in enumerate(approvers):
await client.wait_for_approval(approvers=[approver], key="approval")
// after
for i, approver in enumerate(approvers):
await client.wait_for_approval(approvers=[approver], key=f"approval-{i}") Defensive patterns
Strategy: try-catch
Validate before calling
seen = set()
def unique_key(k: str, seen: set) -> str:
if k in seen:
raise ValueError(f"duplicate WAC step key {k!r}")
seen.add(k)
return k Try / catch
key_counts = {}
key_counts[k] = key_counts.get(k, 0) + 1
if key_counts[k] > 1:
k = f"{k}-{key_counts[k]}" # pre-deduplicate instead of hitting the error Prevention
- Always interpolate a unique component (index/id) into explicit keys in loops
- Keep a registry of used keys and assert uniqueness before each call
- Omit explicit keys when auto-numbering suffices
- Grep workflows for repeated key= literals before deploying
When it happens
Trigger: Calling wait_for_approval with the same explicit key twice in one WAC workflow, typically in a loop with a fixed key literal or copy-pasted approval steps.
Common situations: Loop-based approvals where the loop index was not interpolated into the key; duplicated approval steps after refactoring; reused template code without renaming the key.
Related errors
- You can't use this function in a standalone script or flow s
- This function can only be called as a flow step
- WAC step key "${options.key}" is already used in this workfl
- You can't use 'request_interactive_slack_approval' function
- {what} must be a non-empty step name without `/` or dot segm
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/50819378a0681897.
Report an issue: GitHub.