Environment
vercel==0.5.8
vercel-workers==0.0.22
- Python 3.12 (Vercel Lambda runtime)
The bug
src/vercel/_internal/workflow/worlds/vercel.py:204 catches vqs_client.DuplicateIdempotencyKeyError:
try:
response = await vqs_client.send_async(...)
return response["messageId"]
except vqs_client.DuplicateIdempotencyKeyError:
return f"msg_duplicate_{idempotency_key or 'unknown'}"
But the installed vercel-workers package doesn't expose that name on vercel.workers.client. When the inner send_async raises anything (e.g. BadRequestError from the queue server), Python evaluates the except clause, fails to resolve the attribute, and raises a secondary:
AttributeError: module 'vercel.workers.client' has no attribute 'DuplicateIdempotencyKeyError'
The original exception is lost and the worker handler crashes. Visible in practice whenever world.queue(...) errors at the HTTP layer.
Why CI didn't catch it
Worth flagging as a structural gap:
-
CI doesn't actually run mypy. [tool.mypy] is configured in pyproject.toml and mypy is in the dev dependency group, but scripts/test.sh is just uv run pytest … — no mypy src/ invocation. The CI job is named "test, including lint and typecheck" but only ruff runs as a lint, and ruff doesn't do cross-module attribute resolution, so undefined vqs_client.<...> access slips through.
-
Even if mypy ran, the config would silence this specific case. [tool.mypy] ignore_missing_imports = true means mypy treats any module without visible type stubs as Any. vercel-workers appears to be closed-source / not on a public GitHub repo, and likely ships without a py.typed marker visible to vercel-py's CI. So every attribute on vercel.workers.client type-checks as Any silently.
Suggested fix (in priority order)
- Reference whatever
vercel-workers actually exports for "idempotency key conflict" — possibly a subclass of BadRequestError, or a generic IdempotencyError. If there's no such class, guard the except with getattr(vqs_client, "DuplicateIdempotencyKeyError", _Unreachable) so the worker doesn't AttributeError on unrelated send failures.
- Wire
mypy src/ into scripts/test.sh and CI, and drop ignore_missing_imports for the vercel.workers.* namespace specifically via [[tool.mypy.overrides]]. (basedpyright is already a dev dep of vercel-workers — the workers maintainers presumably know.) That guards against the next bug of this shape.
Happy to send a PR for either if a maintainer can confirm the intended exception type.
Environment
vercel==0.5.8vercel-workers==0.0.22The bug
src/vercel/_internal/workflow/worlds/vercel.py:204catchesvqs_client.DuplicateIdempotencyKeyError:But the installed
vercel-workerspackage doesn't expose that name onvercel.workers.client. When the innersend_asyncraises anything (e.g.BadRequestErrorfrom the queue server), Python evaluates theexceptclause, fails to resolve the attribute, and raises a secondary:The original exception is lost and the worker handler crashes. Visible in practice whenever
world.queue(...)errors at the HTTP layer.Why CI didn't catch it
Worth flagging as a structural gap:
CI doesn't actually run mypy.
[tool.mypy]is configured inpyproject.tomlandmypyis in thedevdependency group, butscripts/test.shis justuv run pytest …— nomypy src/invocation. The CI job is named "test, including lint and typecheck" but onlyruffruns as a lint, and ruff doesn't do cross-module attribute resolution, so undefinedvqs_client.<...>access slips through.Even if mypy ran, the config would silence this specific case.
[tool.mypy] ignore_missing_imports = truemeans mypy treats any module without visible type stubs asAny.vercel-workersappears to be closed-source / not on a public GitHub repo, and likely ships without apy.typedmarker visible to vercel-py's CI. So every attribute onvercel.workers.clienttype-checks asAnysilently.Suggested fix (in priority order)
vercel-workersactually exports for "idempotency key conflict" — possibly a subclass ofBadRequestError, or a genericIdempotencyError. If there's no such class, guard theexceptwithgetattr(vqs_client, "DuplicateIdempotencyKeyError", _Unreachable)so the worker doesn't AttributeError on unrelated send failures.mypy src/intoscripts/test.shand CI, and dropignore_missing_importsfor thevercel.workers.*namespace specifically via[[tool.mypy.overrides]]. (basedpyrightis already a dev dep ofvercel-workers— the workers maintainers presumably know.) That guards against the next bug of this shape.Happy to send a PR for either if a maintainer can confirm the intended exception type.