Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion docs-internal/engine/napi-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Rules for `rivetkit-typescript/packages/rivetkit-napi/`. The bridge is pure plum
## Payload + error conventions

- `#[napi(object)]` bridge payloads stay plain-data only. If TypeScript needs to cancel native work, use primitives or JS-side polling instead of trying to pass a `#[napi]` class instance through an object field.
- N-API structured errors cross the JS<->Rust boundary by prefix-encoding `{ group, code, message, metadata }` into `napi::Error.reason`, then normalizing that prefix back into a `RivetError` on the other side.
- N-API structured errors cross the JS<->Rust boundary by prefix-encoding `{ group, code, message, metadata, rayId }` into `napi::Error.reason`, then normalizing that prefix back into a `RivetError` on the other side.
- N-API bridge debug logs use stable `kind` plus compact payload summaries, never raw buffers or full request bodies.

## Receive-loop state lifecycle
Expand Down
82 changes: 82 additions & 0 deletions rivetkit-typescript/packages/effect/src/RivetError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export class Forbidden extends Schema.TaggedErrorClass<Forbidden>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -63,6 +66,9 @@ export class ActorNotFound extends Schema.TaggedErrorClass<ActorNotFound>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -95,6 +101,9 @@ export class ActorStopping extends Schema.TaggedErrorClass<ActorStopping>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -127,6 +136,9 @@ export class ActorRestarting extends Schema.TaggedErrorClass<ActorRestarting>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -167,6 +179,9 @@ export class ActionNotFound extends Schema.TaggedErrorClass<ActionNotFound>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -197,6 +212,9 @@ export class ActionTimedOut extends Schema.TaggedErrorClass<ActionTimedOut>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -229,6 +247,9 @@ export class ActionAborted extends Schema.TaggedErrorClass<ActionAborted>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -261,6 +282,9 @@ export class ActorOverloaded extends Schema.TaggedErrorClass<ActorOverloaded>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -293,6 +317,9 @@ export class IncomingMessageTooLong extends Schema.TaggedErrorClass<IncomingMess
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -325,6 +352,9 @@ export class OutgoingMessageTooLong extends Schema.TaggedErrorClass<OutgoingMess
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -357,6 +387,9 @@ export class InvalidEncoding extends Schema.TaggedErrorClass<InvalidEncoding>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -389,6 +422,9 @@ export class InvalidRequest extends Schema.TaggedErrorClass<InvalidRequest>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -421,6 +457,9 @@ export class GuardActorReadyTimeout extends Schema.TaggedErrorClass<GuardActorRe
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -453,6 +492,9 @@ export class GuardActorWakeRetriesExceeded extends Schema.TaggedErrorClass<Guard
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -485,6 +527,9 @@ export class GuardActorRunnerFailed extends Schema.TaggedErrorClass<GuardActorRu
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -517,6 +562,9 @@ export class GuardServiceUnavailable extends Schema.TaggedErrorClass<GuardServic
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -549,6 +597,9 @@ export class GuardActorStoppedWhileWaiting extends Schema.TaggedErrorClass<Guard
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -581,6 +632,9 @@ export class GuardTunnelRequestAborted extends Schema.TaggedErrorClass<GuardTunn
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -613,6 +667,9 @@ export class GuardTunnelMessageTimeout extends Schema.TaggedErrorClass<GuardTunn
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -645,6 +702,9 @@ export class GuardTunnelResponseClosed extends Schema.TaggedErrorClass<GuardTunn
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -677,6 +737,9 @@ export class GuardGatewayResponseStartTimeout extends Schema.TaggedErrorClass<Gu
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return true;
}
Expand Down Expand Up @@ -709,6 +772,9 @@ export class InternalError extends Schema.TaggedErrorClass<InternalError>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -742,6 +808,9 @@ export class ActionErrorDecodeFailed extends Schema.TaggedErrorClass<ActionError
get public() {
return this.rivetError.public;
}
get rayId() {
return this.rivetError.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -782,6 +851,9 @@ export class UnknownUserError extends Schema.TaggedErrorClass<UnknownUserError>(
get public() {
return this.cause.public;
}
get rayId() {
return this.cause.rayId;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -829,6 +901,11 @@ export class UnknownError extends Schema.TaggedErrorClass<UnknownError>(
? this.cause.public
: undefined;
}
get rayId() {
return this.cause instanceof RivetkitErrors.RivetError
? this.cause.rayId
: undefined;
}
get isRetryable(): boolean {
return false;
}
Expand Down Expand Up @@ -987,6 +1064,11 @@ export class RivetError extends Schema.TaggedErrorClass<RivetError>(
return "public" in this.reason ? this.reason.public : undefined;
}

/** Delegates to the underlying reason's `rayId` if present. */
get rayId(): string | undefined {
return "rayId" in this.reason ? this.reason.rayId : undefined;
}

/** Delegates to the underlying reason's `isRetryable` getter. */
get isRetryable(): boolean {
return this.reason.isRetryable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ impl ActorContext {
message: Some(message),
public_: Some(true),
status_code: Some(401),
ray_id: None,
}))
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ struct BridgeRivetErrorPayload {
code: String,
message: String,
metadata: Option<serde_json::Value>,
#[serde(rename = "rayId")]
ray_id: Option<String>,
#[serde(rename = "public")]
public_: Option<bool>,
#[serde(rename = "statusCode")]
Expand All @@ -300,6 +302,7 @@ pub(crate) struct BridgeRivetErrorContext {
pub message: Option<String>,
pub public_: Option<bool>,
pub status_code: Option<u16>,
pub ray_id: Option<String>,
}

impl std::fmt::Display for BridgeRivetErrorContext {
Expand Down Expand Up @@ -1047,6 +1050,7 @@ fn parse_bridge_rivet_error(reason: &str) -> Option<anyhow::Error> {
message: Some(message),
public_: payload.public_,
status_code: payload.status_code,
ray_id: payload.ray_id,
}))
}

Expand Down
1 change: 1 addition & 0 deletions rivetkit-typescript/packages/rivetkit-napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ fn anyhow_to_bridge_rivet_error_payload(error: anyhow::Error) -> serde_json::Val
"code": error.code(),
"message": error.message(),
"metadata": error.metadata(),
"rayId": bridge_context.and_then(|context| context.ray_id.as_deref()),
"public": public_,
"statusCode": status_code,
"actor": error.actor(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ mod moved_tests {
"code": "same_code",
"message": "same message",
"metadata": { "count": 1 },
"rayId": "ray-123",
})
);

Expand All @@ -78,6 +79,12 @@ mod moved_tests {
assert!(transport_error(&first).schema().is_none());
assert_eq!(transport_error(&second).group(), "actor");
assert_eq!(transport_error(&second).code(), "same_code");

let payload = crate::anyhow_to_bridge_rivet_error_payload(first);
assert_eq!(
payload.get("rayId").and_then(|value| value.as_str()),
Some("ray-123")
);
}

#[test]
Expand Down
Loading
Loading