Skip to content

Hold an actor's RPC exceptions behind its output gate - #7747

Open
ry wants to merge 3 commits into
cloudflare:mainfrom
ry:rpc-output-gate
Open

ry wants to merge 3 commits into
cloudflare:mainfrom
ry:rpc-output-gate

Conversation

@ry

@ry ry commented Oct 14, 2026 •

Copy link
Copy Markdown

JsRpcTargetBase::callImpl() made an actor's RPC result wait for the output gate, but not its exception. A method could throw an error carrying state whose write was not yet confirmed, and the caller saw it even if that write then failed. WorkerEntrypoint::request() already gates a fetch handler's exceptions.

Wait for the gate on rejection too, and turn a synchronous JS throw into a rejection so it takes the same path. KJ exceptions from decoding the arguments propagate unchanged.

No compatibility flag: only the timing of an error changes, as fetch already does.

JsRpcTargetBase::callImpl() made an actor's RPC result wait for the
output gate, but not its exception. A method could throw an error
carrying state whose write was not yet confirmed, and the caller saw
it even if that write then failed. WorkerEntrypoint::request()
already gates a fetch handler's exceptions.

Wait for the gate on rejection too, and turn a synchronous JS throw
into a rejection so it takes the same path. KJ exceptions from
decoding the arguments propagate unchanged.

No compatibility flag: only the timing of an error changes, as fetch
already does.
@ry
ry requested review from a team as code owners October 14, 2026 18:49
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@ry ry changed the title rpc output gate Hold an actor's RPC exceptions behind its output gate Oct 14, 2026
@ry

ry commented Oct 14, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Oct 14, 2026
Comment thread src/workerd/api/worker-rpc.c++ Outdated
Comment thread src/workerd/api/worker-rpc.c++ Outdated
JSG_CATCH handles the termination check and releases its TryCatch before
the handler runs, replacing the hand-written v8::TryCatch. A KJ exception
from decoding the arguments is rethrown ahead of JSG_CATCH, so it still
propagates unchanged rather than being converted to JavaScript.
Comment thread src/workerd/api/worker-rpc.c++ Outdated
Comment on lines +2387 to +2389
} catch (kj::Exception&) {
throw;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSG_CATCH should cover this (the catch block for kj::Exception&) should not be needed.

But also, just as a general point, we don't use the bare throw;. When this is needed we would use kj::throwFatalException(...).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kj::Exception clause is needed. JSG_CATCH is catch (...), and it converts a kj::Exception to JS through exceptionToJs(). A KJ exception here comes from decoding the arguments, not from the method, so it has to propagate unchanged. If I remove the clause, jsrpc-call-plan-test.c++:1216 fails because the caller gets jsg.Error: internal error; reference = ... instead of the original exception.

I've switched the bare throw; to kj::throwFatalException(kj::mv(e)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants