Skip to content

world.queue passes delay_seconds as float; queue server rejects with "must be an integer between 0 and 86400" #125

Description

@kalyanaramansanthanam

Environment

  • vercel==0.5.8
  • Python 3.12 (Vercel Lambda runtime)

The bug

The workflow runtime computes the delay for a vercel.workflow.sleep wakeup as:

# src/vercel/_internal/workflow/runtime.py:441
seconds = (sus.resume_at - now).total_seconds()

That's a float. worlds/vercel.py:237 clamps it (also float math) and passes it through unchanged to vqs_client.send_async(... delay_seconds=...). The Vercel queue server then rejects:

{"error":"Invalid Vqs-Delay-Seconds. Must be an integer between 0 and 86400 seconds (cannot exceed retention time)"}

So the very first sleep() wakeup queue-send 400s.

Repro

from vercel.workflow import sleep
from vercel._internal.workflow import Workflows

wf = Workflows()

@wf.step
async def noop_step(*, payload: dict) -> None:
    return

@wf.workflow
async def my_workflow(*, payload: dict) -> None:
    await noop_step(payload=payload)
    await sleep("60 seconds")  # → world.queue(..., delay_seconds=60.0) → 400

The chain self-heals eventually (after resume_at is in the past, min_timeout_seconds flips negative and no delay_seconds is sent), but each cycle takes ~5 minutes of queue redeliveries and floods the worker logs with 500s/400s. Compounded by #(other-issue-id, the DuplicateIdempotencyKeyError AttributeError) which masks the original BadRequestError.

Suggested fix

int(delay_seconds) (or math.ceil if you want to err on the side of "wake no earlier than requested") before passing it to the queue client. Fractional seconds are meaningless at the queue's per-second granularity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions