denoland/celldPublic

self-hosted, distributed Durable Objects

Nested waitUntil() rejects during active background work after a Durable Object response returns

DL

**Version:** celld 0.4.1

A Durable Object registers background work with `ctx.waitUntil()` and returns its HTTP response. While that background work is still running, calling the imported `waitUntil()` from `cloudflare:workers` throws:

```text Disallowed operation called within global scope. ```

This is active request-associated background work, not module/global initialization.

### Reduced reproduction

```js import { waitUntil } from "cloudflare:workers";

export class Repro { constructor(ctx) { this.ctx = ctx; }

fetch() { this.ctx.waitUntil( (async () => { await new Promise((resolve) => setTimeout(resolve, 10));

// Runs after the response returns, while the outer task is pending. const pending = fetch("https://example.com"); waitUntil(pending); // Throws the global-scope error. })(), );

return new Response("accepted"); } } ```

### Expected behavior

Additional `waitUntil()` work can extend the lifetime while previously registered background work remains pending.

### Actual behavior

Registration is rejected after the response returns. Once the original background task settles, unfinished native operations are canceled.

### Impact

Our background OpenTelemetry exporter catches the registration exception and returns without awaiting its export. Celld then cancels both the exporter fetch and its timeout. The collector can receive spans followed by `context canceled`, while the application receives neither an export result nor a timeout.

### Verified workaround

Return the export promise from our observer callback, allowing the already-registered outer background task to await it.

  1. bartlomiejuclosedlast month on GitHub

  2. BA

    Thanks for the report, this is now fixed and will be released in the next version