Skip to content

fix(rivetkit): inspector reports actual config state + real queue messages#4726

Closed
NathanFlurry wants to merge 1 commit into04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_dropfrom
04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages
Closed

fix(rivetkit): inspector reports actual config state + real queue messages#4726
NathanFlurry wants to merge 1 commit into04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_dropfrom
04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

NathanFlurry commented Apr 24, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown

claude Bot commented Apr 24, 2026

Code Review: fix(rivetkit): inspector reports actual config state + real queue messages

Overview

This PR fixes two inspector bugs: hardcoded is_state_enabled: true and is_database_enabled tied to infrastructure presence rather than user intent, plus a stub /inspector/queue endpoint that always returned empty messages. It also adds an AbortOnDropTask RAII guard for the WebSocket overlay task.

Positive Changes

  • AbortOnDropTask RAII wrapper (inspector_ws.rs) is an excellent improvement. It closes the leak path where on_close never fires (e.g. during actor teardown) and the overlay task persists. The Drop impl is the right mechanism here.
  • enabled flag on SqliteDb correctly decouples user opt-in (db({...})) from infrastructure presence. The comment explains the "why" well and the Default derive (bool defaults to false) handles unconfigured paths correctly.
  • has_state / has_database propagation through ActorConfig to ActorConfigInput to JsActorConfig is clean and consistent. The unwrap_or(false) defaults are appropriate.
  • The queue endpoint rewrite delivers real data instead of messages: [] with truncated: false.

Issues and Suggestions

1. Silent clamping in u64_to_i64 (rivetkit-napi/src/queue.rs)

The comment claims IDs "fit comfortably in i64", but unwrap_or(i64::MAX) silently returns a misleading sentinel on overflow with no log trail. Consider tracing::warn! before the fallback, or expect with a message if the invariant is truly guaranteed.

2. has_state detection edge case (native.ts)

config.state !== undefined evaluates to true if state: null is passed explicitly, because null !== undefined. This could falsely enable the inspector state tab. Worth confirming null cannot appear at this call site.

3. Comment style nit (inspector_ws.rs)

Per CLAUDE.md, comments should avoid parenthetical constructions. The AbortOnDropTask doc uses (for example when the handler is dropped due to actor teardown rather than a clean close frame). Two plain sentences would be cleaner.

4. Client-side pagination on /inspector/queue (native.ts)

Fetching all messages then slicing is acceptable given the bounded max_queue_size (default 1000). The size/maxSize distinction is semantically correct.

5. Missing test coverage

No tests accompany these changes. At minimum before merging:

  • A driver test asserting is_database_enabled: false for an actor without db({...}).
  • A driver test asserting is_state_enabled: false for a stateless actor.
  • A driver test enqueuing messages and verifying /inspector/queue returns them rather than an empty list.

Summary

The core fixes are correct and well-reasoned. AbortOnDropTask is a genuine improvement over the previous manual abort-in-on_close pattern. Main concerns before graduating from DRAFT: the silent i64::MAX clamping in u64_to_i64, the null edge case in has_state detection, and absence of test coverage.

@github-actions
Copy link
Copy Markdown
Contributor

Preview packages published to npm

Install with:

npm install rivetkit@pr-4726

All packages published as 0.0.0-pr.4726.71415c6 with tag pr-4726.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-71415c6
docker pull rivetdev/engine:full-71415c6
Individual packages
npm install rivetkit@pr-4726
npm install @rivetkit/react@pr-4726
npm install @rivetkit/rivetkit-napi@pr-4726
npm install @rivetkit/workflow-engine@pr-4726

@claude
Copy link
Copy Markdown

claude Bot commented Apr 24, 2026

PR Review: fix(rivetkit): inspector reports actual config state + real queue messages

Overview

This PR fixes two inspector correctness bugs and adds real queue message inspection:

  1. State/database capability flags were hardcoded to true in all inspector responses — the PR plumbs has_state and has_database through ActorConfig so the inspector only shows those tabs when the user actually declared them.
  2. /inspector/queue was returning an empty messages array and truncated: false unconditionally — now it fetches real messages via new Queue::inspect_messages / Queue::max_size NAPI surface.
  3. AbortOnDropTask RAII wrapper ensures the overlay WebSocket task cannot outlive its handler if on_close never fires.

The fix is targeted and surgical — good scope discipline.


Issues

Memory: all messages are fetched before JS-level slicing

// native.ts
const allMessages = await queue.inspectMessages(); // fetches entire queue
const truncated = allMessages.length > limit;
const messages = allMessages.slice(0, limit);      // limit applied here

list_message_entries already accepts a ListOpts { limit } parameter but passes None (no limit). For actors with large queues this allocates and deserializes every message only to discard most of them. The limit should be threaded down to list_message_entries and into the KV call instead. You can still report the total size separately without fetching all bodies — the metadata size field is already maintained.

Side effects of list_messages during inspection

inspect_messages delegates to list_messages, which mutates shared metadata as a side effect (queue_metadata.size and next_id are reconciled on every call). Invoking the inspector endpoint can trigger surprising state updates on an otherwise idle actor. A read-only inspection path that queries KV without touching in-memory metadata would be cleaner. At minimum the side effect should be documented on inspect_messages.

Misleading comment on the ID conversion

/// Queue message id. Stored as the raw u64 reinterpreted as i64 …
pub id: i64,

i64::try_from(value).unwrap_or(i64::MAX) is a range-checked numeric conversion, not a bit-reinterpretation. A bit-cast (value as i64) would silently wrap large IDs and produce negative numbers. The current code is safer (it clamps), but the comment is inaccurate. Suggest: "Numeric conversion of the u64 id; ids are monotonically increasing and fit well within i64 range."


Minor notes

  • AbortOnDropTask is a clean pattern. The refactor is correct — dropping from on_close was already equivalent, but the RAII wrapper correctly handles teardown-without-close-frame.
  • has_state detection logic (config.state !== undefined || typeof config.createState === 'function') looks correct for both the static and factory forms.
  • Limit sanitization in the queue endpoint (rejecting non-finite/non-positive values, flooring to int) is solid.
  • Style: the comment on enabled in SqliteDb has a semicolon run-on sentence. Per CLAUDE.md comments should be complete, normal sentences — minor, but worth a quick pass.

Summary

The core intent is correct and the fixes are needed. The main concern worth addressing before merge is the full-table-scan-then-slice pattern in the queue inspector path — it will become a latency and memory issue for actors with large queues. Everything else is minor.

@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from 51a0ff9 to e0c30f6 Compare April 24, 2026 08:43
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from 20251b0 to 30e8ba6 Compare April 24, 2026 08:43
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from 30e8ba6 to ad4d92a Compare April 24, 2026 09:52
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from e0c30f6 to 94270d3 Compare April 24, 2026 09:52
@NathanFlurry NathanFlurry mentioned this pull request Apr 24, 2026
11 tasks
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from ad4d92a to be354a6 Compare April 24, 2026 10:19
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from 94270d3 to 9009f05 Compare April 24, 2026 10:19
@claude
Copy link
Copy Markdown

claude Bot commented Apr 24, 2026

PR Review: fix(rivetkit): inspector reports actual config state + real queue messages

Overview

This PR fixes two inspector correctness bugs and one RAII leak:

  1. Inspector flags - is_state_enabled and is_database_enabled were hardcoded to true; they now reflect the actual actor config (has_state / has_database).
  2. Queue inspector - /inspector/queue previously returned a stub (empty messages: [], truncated: false); it now calls queue.inspectMessages() for real data.
  3. AbortOnDropTask RAII guard - the overlay task JoinHandle is now wrapped so it is aborted even if on_close never fires.

All three are solid, targeted bug fixes. A few notes below.


Issues / Suggestions

1. u64_to_i64 comment vs. actual behavior

The word "reinterpreted" implies a bitcast/transmute; the actual behavior is a narrowing conversion with i64::MAX as a sentinel fallback. If an id ever naturally equals i64::MAX, the two cases become indistinguishable. Consider clarifying to "narrowed" or "clamped" and documenting the sentinel value.

2. inspect_messages fetches all messages before slicing

All messages are fetched from core then sliced in TypeScript. For the default max queue size (1000) this is fine for an inspector endpoint, but the limit could be pushed down into the Rust API as a follow-up to avoid the fetch-all cost.

3. size field now computed at a different time than before

Before this PR, size came from the inspector snapshot; now it is allMessages.length. These should always agree, but the field is now computed at a slightly different point in time than maxSize. Not a practical concern, just worth noting for anyone who hits a transient race in tests.

4. Visibility widening on inspect_messages / max_size

Both were pub(crate) and are now pub. This is intentional (NAPI needs them), but it expands the API surface of rivetkit-core. If there is a doc-hidden or semver-unstable convention for cross-crate-but-internal methods in this codebase, it may be worth applying it here.

5. on_close guard is now silent without a comment

The explicit abort call was removed and the .take() now relies on the Drop impl. This is correct. A brief inline comment ("abort happens via AbortOnDropTask::drop") would help the next reader understand this is intentional, not a missing abort.


Positives

  • AbortOnDropTask - clean RAII pattern, well-commented, solves a real leak where on_close might not fire during actor teardown.
  • SqliteDb::enabled flag - correctly identifies that runtime_config().is_ok() was an unreliable proxy because the envoy always provisions SQLite. The field comment explains the reasoning clearly.
  • Limit validation in the queue endpoint (Number.isFinite + parsedLimit > 0 + Math.floor) covers the important edge cases (NaN, Infinity, negative, float).
  • No new silent no-ops - inspector paths now reflect real configuration state rather than optimistic defaults, consistent with the fail-by-default principle in CLAUDE.md.

Status

DRAFT - no tests yet (expected for a draft). Once tests covering the queue inspector endpoint and the is_state_enabled/is_database_enabled flags are added, this looks ready to merge.

@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from 9009f05 to 0e8d52d Compare April 24, 2026 10:32
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from be354a6 to 5b61e8e Compare April 24, 2026 10:32
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from 5b61e8e to d1095c3 Compare April 24, 2026 11:48
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from 32b9abd to 8e8783c Compare April 24, 2026 12:14
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from d1095c3 to 8a4bc0d Compare April 24, 2026 12:14
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit_inspector_reports_actual_config_state_real_queue_messages branch from 8a4bc0d to a96fbd9 Compare April 24, 2026 12:32
@NathanFlurry NathanFlurry force-pushed the 04-23-fix_rivetkit-napi_plumb_ctx_through_serializestate_tsf_and_stop_panicking_on_runtime_state_ref_drop branch from 8e8783c to 81178fa Compare April 24, 2026 12:32
@NathanFlurry
Copy link
Copy Markdown
Member Author

Landed in main via stack-merge fast-forward push. Commits are in main; closing to match.

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.

1 participant