fix(rivetkit): preserve internal bridge errors#4993
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
PR #4993 Review: fix(rivetkit): preserve internal bridge errorsOverviewThis PR fixes a significant correctness problem: structured RivetError values thrown by actors were being lost when they crossed the NAPI bridge into Rust, causing all actor errors to appear as opaque internal_error. The fix introduces a new ActorSpecifier type that travels with errors through the whole stack, replaces Box::leak-based schema interning with a RivetErrorKind::Dynamic variant, adds client-boundary sanitization helpers, and bumps the client protocol to v4 to carry actor context back to callers. The design is sound and the changes are well-scoped. A few items worth discussing below. Correctnesscallback_error loses the JsCallbackUnavailable type for unstructured errors Previously, a non-bridge, non-closing NAPI error returned is_internal_error hardcodes group names
actor_specifier() silently drops context when sleep_generation() is None
Memory Leak Fix (Positive)The removal of LoggingA significant number of
New logging is added in ProtocolThe v3 to v4 migration looks correct:
One minor note: the CBOR manual encoder computes the Test CoverageGood additions:
Missing coverage:
Minor
SummaryThe core design is correct and the memory-leak fix is a clear improvement. The main risk is the |
5613480 to
814b9ab
Compare
7685070 to
077bd4e
Compare
814b9ab to
811f9c5
Compare
811f9c5 to
b24f3be
Compare
eb0a964 to
395aa83
Compare
f5ede57 to
ac0098e
Compare
ac0098e to
7d0b73c
Compare
7d0b73c to
d754df5
Compare
e8f7214 to
4e070ea
Compare
PR Review: fix(rivetkit): preserve internal bridge errorsOverviewThis is a substantial, well-structured PR that improves error handling across the full Rust/TypeScript stack. The main changes are:
Issues1. Redundant
response.map(|mut response| {
attach_actor_response_headers(&mut response, &actor);
response
})Because the header map uses 2. Incomplete CRLF injection check in fn attach_actor_response_headers(response: &mut HttpResponse, actor: &ActorSpecifier) {
response.headers.insert(HEADER_RIVET_ACTOR.to_owned(), actor.actor_id.clone()); // no check
...
if !key.contains('\r') && !key.contains('\n') { // only key is guarded
response.headers.insert(HEADER_RIVET_ACTOR_KEY.to_owned(), key.clone());
}
}
3. The macro 4. Logging removed from The PR strips several structured log lines from These were the only log points covering JavaScript→Rust callback failures without a structured bridge error. Without them, untagged JS errors (non- 5.
const { group, code } = deconstructError(error, false);
logger().error({ msg: "failed to bind dynamic hibernatable websocket", ... });The error details ( Observations / Non-blocking
SummaryThe core design is sound and the changes are well-motivated. The two items worth fixing before merge are the CRLF injection gap for |
4e070ea to
16c6a31
Compare
d754df5 to
0889a3a
Compare

Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
Type of change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes.
Checklist: