From f5907de56d1c96b08ac5c75cdf4c4191651229d7 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Wed, 5 Nov 2025 12:33:34 -0800 Subject: [PATCH] fix(rivetkit): fix actors trying to sleep after stop started --- engine/sdks/typescript/runner/src/mod.ts | 8 ++++++++ .../packages/rivetkit/src/actor/instance.ts | 15 +++++++++++++-- .../rivetkit/src/drivers/engine/actor-driver.ts | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/engine/sdks/typescript/runner/src/mod.ts b/engine/sdks/typescript/runner/src/mod.ts index 66a5be6580..c80a201649 100644 --- a/engine/sdks/typescript/runner/src/mod.ts +++ b/engine/sdks/typescript/runner/src/mod.ts @@ -172,6 +172,13 @@ export class Runner { // MARK: Manage actors sleepActor(actorId: string, generation?: number) { + if (this.#shutdown) { + this.log?.warn({ + msg: "runner is shut down, cannot sleep actor", + }); + return; + } + const actor = this.getActor(actorId, generation); if (!actor) return; @@ -860,6 +867,7 @@ export class Runner { intentType: "sleep" | "stop", ) { if (this.#shutdown) { + console.trace("send actor intent", actorId, intentType); this.log?.warn({ msg: "Runner is shut down, cannot send actor intent", }); diff --git a/rivetkit-typescript/packages/rivetkit/src/actor/instance.ts b/rivetkit-typescript/packages/rivetkit/src/actor/instance.ts index a2c350eacf..5c90a4c684 100644 --- a/rivetkit-typescript/packages/rivetkit/src/actor/instance.ts +++ b/rivetkit-typescript/packages/rivetkit/src/actor/instance.ts @@ -1920,6 +1920,9 @@ export class ActorInstance { #resetSleepTimer() { if (this.#config.options.noSleep || !this.#sleepingSupported) return; + // Don't sleep if already stopping + if (this.#stopCalled) return; + const canSleep = this.#canSleep(); this.#rLog.debug({ @@ -1979,11 +1982,20 @@ export class ActorInstance { * 4. Engine runner will publish EventActorStateUpdate with ActorStateSTop **/ _startSleep() { + if (this.#stopCalled) { + this.#rLog.debug({ + msg: "cannot call _startSleep if actor already stopping", + }); + return; + } + // IMPORTANT: #sleepCalled should have no effect on the actor's // behavior aside from preventing calling _startSleep twice. Wait for // `_onStop` before putting in a stopping state. if (this.#sleepCalled) { - this.#rLog.warn({ msg: "already sleeping actor" }); + this.#rLog.warn({ + msg: "cannot call _startSleep twice, actor already sleeping", + }); return; } this.#sleepCalled = true; @@ -2069,7 +2081,6 @@ export class ActorInstance { // Clear timeouts if (this.#pendingSaveTimeout) clearTimeout(this.#pendingSaveTimeout); - if (this.#sleepTimeout) clearTimeout(this.#sleepTimeout); if (this.#checkConnLivenessInterval) clearInterval(this.#checkConnLivenessInterval); diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts index 3b138e6dc4..8cfedae1fe 100644 --- a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts @@ -602,7 +602,7 @@ export class EngineActorDriver implements ActorDriver { } async shutdownRunner(immediate: boolean): Promise { - logger().info({ msg: "stopping engine actor driver" }); + logger().info({ msg: "stopping engine actor driver", immediate }); // Clear the ack flush interval if (this.#wsAckFlushInterval) {