cloudflare / cloudflare/workerd
🐛 Bug Report — Runtime APIs: Facet `ctx.storage.setAlarm()` silently breaks SQLite-backed DO actor in local dev
- Dominant language
- C++
- Stars
- 8.7k
- Forks
- 739
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 174
Description
In `wrangler dev`, calling `ctx.storage.setAlarm()` from inside a Durable Object **facet** of a SQLite-backed DO appears to succeed, then asynchronously breaks the whole actor with:
```text
Error: alarms are not yet implemented for SQLite-backed Durable Objects
```
The error does not throw from the `setAlarm()` call. It surfaces as the rejection of an unrelated in-flight RPC into the facet, with no facet-side stack frames.
Minimal repro: https://github.com/firtoz/facet-alarm-repro
## Repro
```bash
git clone https://github.com/firtoz/facet-alarm-repro
cd facet-alarm-repro
npm install
npx wrangler dev
curl 'http://127.0.0.1:8799/repro'
curl 'http://127.0.0.1:8799/repro?facetAlarm=off'
```
Expected:
- `/repro` should either complete, or `setAlarm()` inside the facet should throw synchronously with a clear "facet alarms are unsupported" error.
- `/repro?facetAlarm=off` should complete.
Actual:
- `/repro` returns `ok: false`; the host -> facet RPC rejects with `alarms are not yet implemented for SQLite-backed Durable Objects`.
- `/repro?facetAlarm=off` completes normally.
## Important detail
The facet's `setAlarm()` call logs success before the actor breaks:
```json
{"tag":"leaf:hold:start","ms":4000,"setAlarm":true}
{"tag":"leaf:setAlarm:ok"}
```
So userland cannot catch this at the alarm call site.
## Environment
- Reproduced on wrangler 4.85.0 through 4.100.0.
- `compatibility_date: 2026-04-24`
- SQLite-backed DO via `new_sqlite_classes`
- Facet spawned with `ctx.facets.get(name, () => ({ class: ctx.exports.Leaf }))`
- Linux x64
## Likely source
From workerd source:
- `src/workerd/server/server.c++`: facet actors (`parent != kj::none`) get `ActorSqlite::Hooks::getDefaultHooks()` with a TODO: `Support alarms in facets, somehow`.
- `src/workerd/io/actor-sqlite.c++`: default `Hooks::scheduleRun()` throws `alarms are not yet implemented for SQLite-backed Durable Objects`.
It looks like the output-gate commit path calls `scheduleRun()` after the JS `setAlarm()` call has already resolved, breaking the actor asynchronously.
## Why this matters
Frameworks like `agents` / Think use alarms for keep-alive and scheduling. If such framework code runs inside a facet, the actor can be broken by a successful-looking `setAlarm()` call, and the error appears elsewhere as an unrelated RPC failure.
## Suggested fix
Either:
1. Support facet alarms in local workerd; or
2. Make `setAlarm()` on facet storage throw synchronously at the call site in local dev, with an error that explicitly says alarms are unsupported in facets.
Contributor guide
Assessment
This issue has not been assessed yet.