Skip to content

Conversation

@NathanFlurry
Copy link
Member

No description provided.

@vercel
Copy link

vercel bot commented Nov 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
rivetkit-serverless Ready Ready Preview Comment Nov 13, 2025 2:48am
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
rivet-cloud Ignored Ignored Nov 13, 2025 2:48am
rivet-inspector Ignored Ignored Preview Nov 13, 2025 2:48am
rivet-site Ignored Ignored Preview Nov 13, 2025 2:48am

Copy link
Member Author

NathanFlurry commented Nov 10, 2025

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


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

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

@claude
Copy link

claude bot commented Nov 10, 2025

Code Review for PR #3440

Summary

This PR introduces a protocol capability check for connection drivers to prevent RivetKit protocol messages from being sent to connections that don't support it. This is a good architectural improvement that makes the connection driver interface more explicit about protocol support.

Positive Changes

Clear Protocol Separation: Moving sendMessage into a rivetKitProtocol optional interface makes it explicit which drivers support the RivetKit protocol vs. raw connections.

Prevents Protocol Mismatches: Properly guards against sending RivetKit messages (Init, Event, etc.) to raw HTTP/WebSocket connections that don't understand them.

Consistent Pattern: The check connection[CONN_SPEAKS_RIVETKIT_SYMBOL]() is consistently applied in all the right places:

  • connection-manager.ts:160 - Init message
  • event-manager.ts:219 - Event broadcasts
  • mod.ts:208 - Individual connection sends

Good Logging: Appropriate warning logs when attempting to send to incompatible connections.

Issues & Concerns

1. Bug in conn/mod.ts:208-214 ⚠️

send(eventName: string, ...args: unknown[]) {
    this.#assertConnected();
    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
        this.#actor.rLog.warn({
            msg: "cannot send messages to this connection type",
            connId: this.id,
            connType: this[CONN_DRIVER_SYMBOL]?.type,
        });
    }
    // ... continues to send anyway
}

Problem: The check logs a warning but doesn't return early. This means the code continues to send the message even when the connection doesn't support RivetKit protocol.

Fix: Add an early return after the warning:

if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {  // Also note: missing () call
    this.#actor.rLog.warn({
        msg: "cannot send messages to this connection type",
        connId: this.id,
        connType: this[CONN_DRIVER_SYMBOL]?.type,
    });
    return;  // Add this
}

Also note you're missing the function call () on line 208 - it should be this[CONN_SPEAKS_RIVETKIT_SYMBOL]() not this[CONN_SPEAKS_RIVETKIT_SYMBOL].

2. Typo in Symbol Access 🐛

Line 208 in mod.ts: if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) should be if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) - you're checking the function reference instead of calling it.

The correct implementation is on line 66:

[CONN_SPEAKS_RIVETKIT_SYMBOL](): boolean {
    return this[CONN_DRIVER_SYMBOL]?.rivetKitProtocol !== undefined;
}

3. Silent Failures in Event Broadcasting 🔕

In event-manager.ts:219, when a connection doesn't speak RivetKit, the event is silently skipped. While this might be intentional, it could lead to confusing behavior:

for (const connection of subscribers) {
    if (connection[CONN_SPEAKS_RIVETKIT_SYMBOL]) {  // Silent skip
        try {
            connection[CONN_SEND_MESSAGE_SYMBOL](toClientSerializer);
            sentCount++;
        } catch (error) { ... }
    }
}

Consideration: Should we log a debug message when skipping subscribers? Or should raw connections not be allowed to subscribe to events in the first place?

4. Missing Test Coverage 📝

The changes don't include any tests. Consider adding tests for:

  • WebSocket driver with rivetKitProtocol defined properly sends messages
  • HTTP driver without rivetKitProtocol doesn't receive Init/Event messages
  • conn.send() returns early when connection doesn't support RivetKit
  • Event broadcasts skip non-RivetKit connections

Minor Suggestions

Documentation: Consider adding JSDoc comments to the rivetKitProtocol interface explaining when a driver should implement it vs. when it shouldn't.

Type Safety: The CONN_SPEAKS_RIVETKIT_SYMBOL method could be simplified since it's just checking for presence:

[CONN_SPEAKS_RIVETKIT_SYMBOL](): this is Conn<...> & { [CONN_DRIVER_SYMBOL]: ConnDriver & { rivetKitProtocol: NonNullable<...> } } {
    return this[CONN_DRIVER_SYMBOL]?.rivetKitProtocol !== undefined;
}

Though this might be overly complex for the benefit.

Security Considerations

✅ No security concerns identified. The change properly validates protocol compatibility before sending messages.

Performance Considerations

✅ Minimal performance impact. The checks are simple property accesses and are appropriate guards before more expensive serialization operations.

Overall Assessment

This is a valuable refactoring that makes the driver interface more explicit and prevents protocol mismatches. However, the bug in mod.ts:208-214 must be fixed before merging - currently the send() method will still attempt to send messages to incompatible connections despite the warning.

Recommendation: Fix the two bugs mentioned above, then this is ready to merge. Consider adding basic test coverage for the protocol capability checks.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 10, 2025

More templates

@rivetkit/actor

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/actor@3440

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@3440

@rivetkit/core

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/core@3440

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/db@3440

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@3440

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@3440

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@3440

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@3440

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@3440

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@3440

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@3440

commit: ca5b3f0

@NathanFlurry NathanFlurry changed the base branch from 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events to graphite-base/3440 November 10, 2025 21:58
@claude
Copy link

claude bot commented Nov 13, 2025

PR Review: Skip sending RivetKit messages to conns that do not support it

Summary

This PR introduces a clean architectural improvement by making the RivetKit protocol support explicit and opt-in for connection drivers. This prevents attempting to send protocol-specific messages to connections that don't understand them.

✅ Strengths

  1. Clear Protocol Separation: The new rivetKitProtocol field makes it explicit which connection drivers support the RivetKit client protocol, following the interface segregation principle.

  2. Better Type Safety: Moving sendMessage into the optional rivetKitProtocol object prevents accidental calls to connections that don't support it.

  3. Improved Error Handling: Added guard checks before sending messages to RivetKit-incompatible connections with appropriate debug/warning logs.

  4. Consistent Pattern: The CONN_SPEAKS_RIVETKIT_SYMBOL symbol provides a clean API for checking protocol support throughout the codebase.

🐛 Potential Issues

  1. Early Return Missing in conn/mod.ts:208:

    send(eventName: string, ...args: unknown[]) {
        this.#assertConnected();
        if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
            this.#actor.rLog.warn({
                msg: "cannot send messages to this connection type",
                connId: this.id,
                connType: this[CONN_DRIVER_SYMBOL]?.type,
            });
        }
        // ⚠️ Code continues to execute even if connection doesn't support RivetKit!

    Fix: Add return; after the warning log to prevent attempting to send unsupported messages.

  2. Method Call Syntax: In conn/mod.ts:182, the check should call the method:

    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) {  // Missing ()

    Should be:

    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {

📝 Code Quality Observations

  1. Documentation: The JSDoc comment for rivetKitProtocol is clear and explains the purpose well.

  2. Backwards Compatibility: The change is backwards compatible - existing code that checks for sendMessage directly will now check for rivetKitProtocol instead.

  3. Logging Levels: Good use of debug vs warning levels for different scenarios.

🔒 Security & Performance

  • Security: No security concerns identified.
  • Performance: Minimal impact. The additional checks are simple property access operations.

🧪 Test Coverage

The PR doesn't include test changes. Consider adding tests for:

  • Connections with rivetKitProtocol defined receiving messages correctly
  • Connections without rivetKitProtocol being skipped appropriately
  • The CONN_SPEAKS_RIVETKIT_SYMBOL check working as expected

Recommendation

Approve with minor fixes - The architectural improvement is solid, but the two bugs mentioned above should be fixed before merging to prevent runtime issues.


🤖 Generated with Claude Code

@claude
Copy link

claude bot commented Nov 13, 2025

Code Review - PR #3440

Summary

This PR introduces a protocol capability flag to distinguish connections that support the RivetKit client protocol from those that don't. This is a well-designed improvement that prevents sending RivetKit-specific messages to connections that cannot handle them.


✅ Strengths

  1. Clear API Design: The introduction of rivetKitProtocol as an optional property on ConnDriver makes the distinction explicit and self-documenting.

  2. Backward Compatibility: Existing drivers that don't define rivetKitProtocol will naturally be treated as non-RivetKit connections, which is safe and correct.

  3. Consistent Pattern: The use of a symbol (CONN_SPEAKS_RIVETKIT_SYMBOL) follows the existing pattern in the codebase for accessing internal connection state.

  4. Comprehensive Coverage: The fix properly addresses all relevant code paths:

    • Init messages (connection-manager.ts:160)
    • Direct sends via conn.send() (mod.ts:208)
    • Broadcast events (event-manager.ts:219)

🔍 Issues & Concerns

1. Bug in conn.send() - Missing Early Return (mod.ts:206-214)

Severity: High

The warning log at line 208 does not prevent execution from continuing. This means even after warning, the code will still attempt to serialize and send a RivetKit protocol message to a non-supporting connection.

send(eventName: string, ...args: unknown[]) {
    this.#assertConnected();
    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
        this.#actor.rLog.warn({
            msg: "cannot send messages to this connection type",
            connId: this.id,
            connType: this[CONN_DRIVER_SYMBOL]?.type,
        });
    }
    // ⚠️ Execution continues here regardless!
    this.#actor.inspector.emitter.emit("eventFired", {...});
    // ... serialization and send happens anyway
}

Recommendation: Add an early return after the warning:

if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {
    this.#actor.rLog.warn({
        msg: "cannot send messages to this connection type",
        connId: this.id,
        connType: this[CONN_DRIVER_SYMBOL]?.type,
    });
    return; // Add this!
}

2. Inconsistent Method Call Syntax

Line 208 calls this[CONN_SPEAKS_RIVETKIT_SYMBOL] without parentheses, but line 160 and 219 call it with parentheses ().

Looking at the definition (mod.ts:66-68):

[CONN_SPEAKS_RIVETKIT_SYMBOL](): boolean {
    return this[CONN_DRIVER_SYMBOL]?.rivetKitProtocol !== undefined;
}

It's defined as a method, so it requires parentheses.

Impact: Line 208 will not work correctly - it will always evaluate to truthy (the function object itself) rather than calling the function and getting the boolean result.

Recommendation: Add parentheses at line 208:

if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {

3. Silent Filtering vs Warning

In event-manager.ts:219, the code silently skips non-RivetKit connections when broadcasting:

for (const connection of subscribers) {
    if (connection[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
        try {
            connection[CONN_SEND_MESSAGE_SYMBOL](toClientSerializer);

This is inconsistent with mod.ts:208 which logs a warning.

Question: Should there be a debug log when skipping connections during broadcast? This would help with debugging subscription issues.

Recommendation: Consider adding:

if (connection[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {
    // ... send message
} else {
    this.#actor.rLog.debug({
        msg: "skipping non-RivetKit connection in broadcast",
        eventName: name,
        connId: connection.id,
        connType: connection[CONN_DRIVER_SYMBOL]?.type,
    });
}

4. Missing Parentheses in Other Locations

Both line 160 (connection-manager.ts) and line 219 (event-manager.ts) correctly use connection[CONN_SPEAKS_RIVETKIT_SYMBOL]() with parentheses, but line 208 (mod.ts) does not.


🧪 Test Coverage

No test files were modified or added in this PR. Given the critical nature of message routing logic, consider adding tests for:

  1. Connections with rivetKitProtocol defined receive Init, events, and broadcasts
  2. Connections without rivetKitProtocol are properly skipped
  3. Mixing both connection types in the same actor instance
  4. Calling conn.send() on non-RivetKit connections logs appropriately and doesn't crash

📝 Documentation

The JSDoc comment added at driver.ts:17-23 is excellent and clearly explains when rivetKitProtocol should be defined. Well done!


🎯 Action Items

Must Fix Before Merge:

  1. Add parentheses at mod.ts:208: if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {
  2. Add early return after the warning in conn.send() to prevent attempting to send messages

Nice to Have:

  1. Add debug logging when skipping non-RivetKit connections in broadcast
  2. Add test coverage for the new capability flag behavior
  3. Update any integration tests that might assume all connections receive RivetKit messages

Overall Assessment

This is a solid architectural improvement that properly separates protocol concerns. The main issues are minor bugs in the implementation that will cause incorrect behavior. With the fixes above, this will be ready to merge.

Great work on the clean API design! 🎉

@NathanFlurry NathanFlurry changed the base branch from 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events to graphite-base/3440 November 13, 2025 02:27
@NathanFlurry NathanFlurry force-pushed the 11-10-fix_rivetkit_skip_sending_rivetkit_messages_to_conns_that_do_not_support_it branch from 9416e61 to ca5b3f0 Compare November 13, 2025 02:47
@NathanFlurry NathanFlurry changed the base branch from graphite-base/3440 to 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events November 13, 2025 02:48
@NathanFlurry NathanFlurry changed the base branch from 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events to graphite-base/3440 November 13, 2025 02:50
@claude
Copy link

claude bot commented Nov 13, 2025

PR Review: Skip sending RivetKit messages to connections that do not support it

Summary

This PR introduces a proper type-safe mechanism to identify connections that support the RivetKit protocol and prevents sending RivetKit-specific messages to connections that don't support it. This is a good architectural improvement that makes the connection driver interface more explicit and type-safe.


Code Quality ✅

Strengths:

  • Clean separation of concerns by introducing rivetKitProtocol property on ConnDriver
  • Good use of TypeScript's optional properties to indicate capability
  • Consistent naming with the CONN_SPEAKS_RIVETKIT_SYMBOL symbol
  • Proper documentation added to the new rivetKitProtocol interface

Minor Issues:

  1. Method vs Function Symbol (rivetkit-typescript/packages/rivetkit/src/actor/conn/mod.ts:66-68)

    • CONN_SPEAKS_RIVETKIT_SYMBOL is defined as a method, but it should be called with parentheses
    • Line 208 has a bug: if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) should be if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]())
    • This will always evaluate to true since a function reference is truthy, meaning the warning will never be logged
  2. Missing Early Return (rivetkit-typescript/packages/rivetkit/src/actor/conn/mod.ts:206-214)

    • After logging the warning about unsupported connection types, the code continues to execute
    • Consider adding a return; statement after the warning to prevent attempting to send the message

Potential Bugs 🐛

  1. Critical: Symbol Call Missing Parentheses

    // Current (line 208):
    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
    
    // Should be:
    if (!this[CONN_SPEAKS_RIVETKIT_SYMBOL]()) {

    This means the warning will never trigger, and messages will still be attempted to be sent to non-RivetKit connections.

  2. Logic Issue: No Early Return
    After warning about unsupported connection types in send(), the code still attempts to send the message. This should probably return early to prevent the subsequent message sending logic from executing.


Performance Considerations ✅

  • The changes add minimal overhead (just checking for property existence)
  • Moving sendMessage into a nested object (rivetKitProtocol.sendMessage) doesn't impact performance
  • The symbol-based method is efficient for runtime checks

Security Concerns ✅

No security issues identified. The changes actually improve security by:

  • Preventing protocol messages from being sent to the wrong connection types
  • Making the interface more explicit about capabilities

Test Coverage ⚠️

Missing Test Coverage:

  • No tests verify that non-RivetKit connections don't receive RivetKit protocol messages
  • No tests verify the CONN_SPEAKS_RIVETKIT_SYMBOL method works correctly
  • No tests for the warning behavior when attempting to send to unsupported connections

Recommended Tests:

  1. Test that HTTP and raw-request drivers don't have rivetKitProtocol defined
  2. Test that WebSocket driver has rivetKitProtocol defined
  3. Test that send() warns and skips sending for non-RivetKit connections
  4. Test that Init messages are only sent to RivetKit connections
  5. Test that subscription events are only sent to RivetKit connections

Architecture & Design ✅

Positive Changes:

  • The rivetKitProtocol property makes capabilities explicit at the type level
  • Moving from optional sendMessage? to required rivetKitProtocol?.sendMessage is more semantically correct
  • Consistent checking pattern across connection-manager.ts and event-manager.ts

Suggestions:

  • Consider making CONN_SPEAKS_RIVETKIT_SYMBOL a getter property instead of a method for more idiomatic TypeScript:
    get [CONN_SPEAKS_RIVETKIT_SYMBOL](): boolean {
        return this[CONN_DRIVER_SYMBOL]?.rivetKitProtocol !== undefined;
    }

Recommended Changes

Must Fix:

  1. Fix the bug on line 208: Add parentheses to call the method
  2. Add early return after warning in send() method

Should Consider:

  1. Add test coverage for the new behavior
  2. Consider converting the symbol method to a getter property

Overall Assessment

This is a solid architectural improvement that makes the connection driver interface more explicit. However, there's a critical bug that prevents the intended behavior from working correctly. Once the parentheses are added and tests are written, this will be ready to merge.

Recommendation: Request changes for the critical bug fix, then approve once fixed.


Generated with Claude Code

@NathanFlurry NathanFlurry changed the base branch from graphite-base/3440 to 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events November 13, 2025 03:24
@NathanFlurry NathanFlurry changed the base branch from 11-10-fix_rivetkit_fix_race_condition_with_websocket_open_events to graphite-base/3440 November 13, 2025 03:35
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.

2 participants