windmill-labs/windmill · error · RuntimeError
{what} must be a non-empty step name without `/` or dot segm
Error message
{what} must be a non-empty step name without `/` or dot segments What it means
_assert_usable_step_key validates wait_for_approval step keys: a key travels as one path segment in minted approval URLs, so it must be non-empty and free of '/', backslashes, and dot segments ('.', '..'). Otherwise wait_for_approval would accept a key that get_approval_urls can never address. This RuntimeError fires at call time naming the offending key via the `what` prefix.
Source
Thrown at python-client/wmill/wmill/client.py:2717
name = "main"
return name, schema
# ── Workflow-as-Code SDK ──────────────────────────────────────────────
import asyncio as _asyncio
import contextvars as _contextvars
import sys as _sys
import traceback as _traceback
def _assert_usable_step_key(key: str, what: str) -> None:
"""A step key travels as one path segment when its URLs are minted, so it must be
non-empty and free of ``/`` and dot segments — otherwise ``wait_for_approval``
would accept a key ``get_approval_urls`` can never address."""
k = key.strip()
if not k or k in (".", "..") or "/" in key or "\\" in key:
raise RuntimeError(f"{what} must be a non-empty step name without `/` or dot segments")
class _StepSuspend(BaseException):
"""Raised to suspend workflow execution. Inherits from BaseException
so it is not caught by bare `except Exception:` blocks."""
def __init__(self, dispatch_info: dict):
self.dispatch_info = dispatch_info
class _StepFailure(BaseException):
"""Carries the exception raised by the step a child round executes directly.
That exception *is* the round's result, so a broad ``except Exception`` in the
body must not be able to turn it into a successful complete — the parent would
then record the caught branch's value as the step result. BaseException for the
same reason ``_StepSuspend`` is; a bare ``except:`` still swallows both.
"""View on GitHub (pinned to e474e8803c)
Solutions
- Use a simple identifier: letters, digits, dashes, underscores (e.g. 'manager-approval')
- Sanitize before passing: replace '/' and '\\' with '-', strip whitespace
- Pre-validate with the same rule: k and k not in ('.','..') and '/' not in k and '\\' not in k
- Encode hierarchy in keys with a safe separator (e.g. '--') instead of '/'
Example fix
// before
await client.wait_for_approval(key=f"approvals/{step_id}")
// after
await client.wait_for_approval(key=f"approvals--{step_id}") Defensive patterns
Strategy: validation
Validate before calling
import re
def valid_step_key(k: str) -> str:
k2 = k.strip()
if not k2 or k2 in (".", "..") or "/" in k or "\\" in k:
raise ValueError(f"invalid step key: {k!r}")
return k2 Try / catch
try:
await client.wait_for_approval(key=k)
except RuntimeError as e:
if "must be a non-empty step name" in str(e):
k = re.sub(r"[/\\.]", "-", k).strip("-") or "approval"
await client.wait_for_approval(key=k)
else:
raise Prevention
- Use only [A-Za-z0-9_-] characters in step keys
- Sanitize dynamic keys derived from user input or resource names
- Never interpolate paths or URLs as keys
- Keep a shared key-normalization helper for all wait_for_approval call sites
When it happens
Trigger: Calling wait_for_approval(key='') or whitespace-only, key='a/b', key='..', key='.', or keys containing backslashes.
Common situations: Dynamically building keys from user input or resource names containing slashes; interpolating an empty variable into key; pasting a path segment as a key.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- ${what} must be a non-empty step name without `/` or dot seg
- WAC step key "{requested_key}" is already used in this workf
- result.substring(__RESULT_ERR_PREFIX.length)
- No workflow() entrypoint found. Wrap your main function with
- Invalid migration name '${name}': use only letters, digits,
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/8e65b8fd7fa3d3a9.
Report an issue: GitHub.