cloudflare / cloudflare/developer-platform
`step.sleepUntil` isn't replay safe (nor `step.sleep`)
- 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
Contributor guide
Assessment
This issue has not been assessed yet.