Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document workers I/O errors with examples #16965

Open
wants to merge 2 commits into
base: production
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/content/docs/workers/observability/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,40 @@ export default {
}
```

### "Cannot perform I/O on behalf of a different ..." errors

The error message `Cannot perform I/O on behalf of a different request` or `Cannot perform I/O on behalf of a different Durable Object` is thrown when w Worker attempts to perform certain I/O
operations across different request scopes. This error can be confusing and difficult to debug
as it is an error condition that is very specific to the Workers runtime and the way I/O operations
are handled.

The easiest way to understand what kind of conditions can lead to this error is to look at an example.

This is a rather silly example as is because there's no practical reason why someone would write a Worker that operates like this but it is useful for illustration:

```js
export default {
async fetch(request, env, ctx) {
if (this.resp === undefined) {
globalThis.resp = await fetch('https://example.com');
return new Response("Resuest 1 is ok!");
}

return new Response(globalThis.resp);
}
};
```

The first time a request is made to this Worker, it will initiate a `fetch()` and place the returned `Response` on the global object. The second request received by the Worker will attempt to use that `fetch()` response. However, each of the two requests has its own "I/O Context", which means I/O initiated under each request is isolate from other requests. Or, put more simply: the `Response` object returned by the `fetch()` is *bound* to the first requests I/O Context and will lead to the error `Cannot perform I/O on behalf of a different request` being thrown on the second request.

There are a number of I/O Context bound objects in the runtime: `ReadableStream`, `WritableStream`, `TransformStream`, `AbortSignal` are the most common examples. When these are created within a request they should only be used within the scope of that request.

An common way of triggering this error is to use a global cache for fetches. Specifically, imagine a case where a Worker uses a global cache to store and deduplicate in-flight requests. That is, request one starts a fetch to `https://example.org` and stores the in-flight promise for the response in the global cache. A subsequent request sees that the fetch is in-flight and attempts to attach another `.then(...)` handler to the response promise. This will trigger the error because the second request is trying to perform I/O on behalf of the first request.

To avoid this error, ensure that I/O operations are scoped to the request that initiated them. If you need to share data between requests, consider using a Durable Object, Workers KV, or some other form of shared storage.

Under certain conditions, the runtime may emit a warning: `A promise was resolved or rejected from a different request context than the one it was created in. However, the creating request has already been completed or canceled. Continuations for that request are unlikely to run safely and have been canceled.` This warning specifically indicates that a JavaScript promise originating from one request has been resolved or rejected from another request. However, the original request has been canceled or completed making it unsafe to actually scheduling any promise continuations that would have otherwise been scheduled by resolving the promise (a continuation is any code that follows an `await` statement or any code running in a `.then(...)`, `.catch(...)`, or `.finally(...)` handler). When this warning is emitted, the runtime will not schedule any continuations for the promise that was resolved or rejected. This signals a bug in the Worker code that should be fixed. An attempt will be made to print the stack trace identifying where the promise was resolved or rejected.

## Errors on Worker upload

These errors occur when a Worker is uploaded or modified.
Expand Down
Loading