From ac12ef80b0f2601b9e320f51a8396926ec49410e Mon Sep 17 00:00:00 2001 From: ABCxFF <79597906+abcxff@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:29:12 +0000 Subject: [PATCH] fix(rivetkit): stop logging benign actor-sleep aborts at error level --- .../rivetkit-napi/src/napi_actor_events.rs | 20 ++++++++++++++----- .../packages/rivetkit/src/actor/errors.ts | 11 ++++++++++ .../packages/rivetkit/src/registry/native.ts | 7 ++----- .../packages/rivetkit/src/workflow/mod.ts | 6 ++++-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/rivetkit-typescript/packages/rivetkit-napi/src/napi_actor_events.rs b/rivetkit-typescript/packages/rivetkit-napi/src/napi_actor_events.rs index cd72127017..34cf0b172b 100644 --- a/rivetkit-typescript/packages/rivetkit-napi/src/napi_actor_events.rs +++ b/rivetkit-typescript/packages/rivetkit-napi/src/napi_actor_events.rs @@ -954,11 +954,21 @@ fn spawn_run_handler( ); } Err(error) => { - tracing::error!( - actor_id = %ctx.inner().actor_id(), - ?error, - "napi run handler failed" - ); + let extracted = RivetTransportError::extract(&error); + if extracted.group() == "actor" && extracted.code() == "aborted" { + // The run handler was unwound because the actor is going to + // sleep. This is the expected exit, not a failure. + tracing::debug!( + actor_id = %ctx.inner().actor_id(), + "napi run handler aborted for sleep" + ); + } else { + tracing::error!( + actor_id = %ctx.inner().actor_id(), + ?error, + "napi run handler failed" + ); + } } } }) diff --git a/rivetkit-typescript/packages/rivetkit/src/actor/errors.ts b/rivetkit-typescript/packages/rivetkit/src/actor/errors.ts index af925c1fa5..71ef418821 100644 --- a/rivetkit-typescript/packages/rivetkit/src/actor/errors.ts +++ b/rivetkit-typescript/packages/rivetkit/src/actor/errors.ts @@ -98,6 +98,17 @@ export function isRivetErrorLike( ); } +// Matches the `actor`/`aborted` error that core raises when an in-flight queue +// wait is interrupted because the actor is going to sleep. This is the expected +// way a parked `run` handler unwinds during sleep, not a failure. +export function isActorAbortedError(error: unknown): boolean { + return ( + isRivetErrorLike(error) && + error.group === "actor" && + error.code === "aborted" + ); +} + function isActorSpecifier(value: unknown): value is ActorSpecifier { return ( typeof value === "object" && diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts index fa2dddf8a9..4d7438ec00 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts @@ -13,6 +13,7 @@ import { encodeBridgeRivetError, forbiddenError, INTERNAL_ERROR_CODE, + isActorAbortedError, isRivetErrorLike, RivetError, type RivetErrorLike, @@ -1802,11 +1803,7 @@ class NativeQueueAdapter { } yield message; } catch (error) { - if ( - isRivetErrorLike(error) && - error.group === "actor" && - error.code === "aborted" - ) { + if (isActorAbortedError(error)) { return; } throw error; diff --git a/rivetkit-typescript/packages/rivetkit/src/workflow/mod.ts b/rivetkit-typescript/packages/rivetkit/src/workflow/mod.ts index 2b538d56d7..ade9fc8ded 100644 --- a/rivetkit-typescript/packages/rivetkit/src/workflow/mod.ts +++ b/rivetkit-typescript/packages/rivetkit/src/workflow/mod.ts @@ -20,7 +20,7 @@ import { RUN_FUNCTION_CONFIG_SYMBOL, } from "@/actor/config"; import type { AnyStaticActorInstance } from "@/actor/definition"; -import { RivetError } from "@/actor/errors"; +import { isActorAbortedError, RivetError } from "@/actor/errors"; import type { EventSchemaConfig, QueueSchemaConfig } from "@/actor/schema"; import type { AnyDatabaseProvider } from "@/common/database/config"; import { stringifyError } from "@/utils"; @@ -256,7 +256,9 @@ export function workflow< try { await handle.result; } catch (error) { - if (runCtx.abortSignal.aborted) { + // `abortSignal.aborted` is delivered on a separate async hop and + // races the rejection, so detect the sleep abort structurally too. + if (runCtx.abortSignal.aborted || isActorAbortedError(error)) { return; }