cloudflare / cloudflare/developer-platform

`step.sleepUntil` isn't replay safe (nor `step.sleep`)

Open
#51 4 comments 0 reactions 1 assignee Claimed by @LuisDuarte1 View on GitHub
product:workflows
Dominant language
No language data
Stars
1
Forks
0
PR merge metrics
No merged PRs in 30d

Description

### What versions & operating system are you using?

Binaries:
Node: 24.11.1 - /home/esteban/.nvm/versions/node/v24.11.1/bin/node
npm: 11.6.2 - /home/esteban/.nvm/versions/node/v24.11.1/bin/npm
pnpm: 10.30.3 - /home/esteban/.nvm/versions/node/v24.11.1/bin/pnpm
bun: 1.3.9 - /home/esteban/.nvm/versions/node/v24.11.1/bin/bun
npmPackages:
@cloudflare/vite-plugin: ^1.25.6 => 1.25.6
wrangler: ^4.69.0 => 4.69.0

### Please provide a link to a minimal reproduction

/

### Describe the Bug

Hello,

The key used in `step.sleep` depends on the duration:
[https://github.com/cloudflare/workers-sdk/blob/077c4d54cbf068aeca86927620332b9aeceec762/packages/workflows-shared/src/context.ts#L520](https://github.com/cloudflare/workers-sdk/blob/077c4d54cbf068aeca86927620332b9aeceec762/packages/workflows-shared/src/context.ts#L520)

This is not necessarily an issue when using a fixed duration such as `1 hour`, even though it is somewhat questionable because the documentation explicitly states that only the step name is used to compute the key:
[https://developers.cloudflare.com/workflows/build/rules-of-workflows/#name-steps-deterministically](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#name-steps-deterministically)

However, it becomes a real issue when the duration changes.

The method `step.sleepUntil` is a wrapper around `step.sleep` (see: [https://github.com/cloudflare/workers-sdk/blob/077c4d54cbf068aeca86927620332b9aeceec762/packages/workflows-shared/src/context.ts#L601](https://github.com/cloudflare/workers-sdk/blob/077c4d54cbf068aeca86927620332b9aeceec762/packages/workflows-shared/src/context.ts#L601)) and relies on `Date.now`:

```ts
this.sleep(name, timestamp - now)
```

This line is the most problematic. Because the duration changes over time, the computed key will also change, meaning the workflow can never reuse the same step key and therefore cannot progress past this step.

For example:

1. Start a workflow.
2. Reach a line with `step.sleepUntil('sleep', )`.
3. The workflow sleeps.
4. When it wakes up, the workflow replays all steps and reaches `this.sleep(name, timestamp - now)` again.

At that point, the duration `timestamp - now` is shorter than it was during the first execution, so the key is different.

In my case, I compute `` to be 7:00 AM, or the next day at 7:00 AM if the time has already passed (see the code screenshot). Because the workflow wakes up slightly after 7:00 AM (usually a few seconds later), the key cannot be reused. As a result, the workflow schedules another sleep for a full day, and this repeats indefinitely.

So, I'm not sure if this is a bug in the worker-sdk or the desired behavior and I should change my code?

### Please provide any relevant error logs

Image

Image

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.