-
Notifications
You must be signed in to change notification settings - Fork 129
fix(rivetkit): don't throw error if websocket is not open, causing realtime to break after a connection disconnects #3316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
3 Skipped Deployments
|
More templates
@rivetkit/actor
@rivetkit/cloudflare-workers
@rivetkit/core
@rivetkit/db
@rivetkit/framework-base
@rivetkit/next-js
@rivetkit/react
rivetkit
@rivetkit/sql-loader
@rivetkit/engine-runner
@rivetkit/engine-runner-protocol
commit: |
Pull Request ReviewSummaryThis PR adds a defensive check to prevent errors when attempting to send messages to closed WebSocket connections. The fix is sound and addresses a real issue where the realtime system would break after connection disconnects. Code Quality: ✅ GoodPositive aspects:
Minor suggestions:
Potential Issues:
|
20316f6 to
8872a11
Compare
358af35 to
5d3cea0
Compare
PR Review: WebSocket Connection State GuardSummaryThis PR adds a defensive check to prevent sending messages to closed WebSocket connections, fixing a bug where realtime broadcasts could attempt to send messages to disconnected clients. Positive Feedback✅ Addresses a Real Race Condition: The fix correctly handles the scenario where messages are broadcast to subscribed connections (line 1796 in ✅ Good Defensive Programming: Checking ✅ Proper Structured Logging: The warning log follows the project's conventions from CLAUDE.md:
✅ Appropriate Severity: Using Suggestions for Improvement1. Consider Consistency with Other DriversThe SSE driver doesn't have a similar check in its // SSE_DRIVER.sendMessage currently doesn't check stream state
state.stream.writeSSE({ data: encodeDataToString(message.serialize(state.encoding)) });Could if (state.stream.aborted || state.stream.closed) {
actor.rLog.warn({ msg: "attempting to send message to closed SSE stream", connId: conn.id });
return;
}2. State Check Could Be More ComprehensiveCurrently only checks for if (state.websocket.readyState \!== ConnReadyState.OPEN) {
// Current implementation - good\!
}This is already correct, but you might want to add more context about why it's not open: actor.rLog.warn({
msg: "attempting to send message to closed websocket, this is likely a bug in RivetKit",
connId: conn.id,
wsReadyState: state.websocket.readyState,
wsReadyStateName: ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'][state.websocket.readyState] // More readable
});3. Root Cause InvestigationThe log message says "this is likely a bug in RivetKit". While the defensive check is good, consider if there's a root cause to address:
Example potential improvement in // Send message to clients
for (const connection of subscriptions) {
// Skip connections that aren't ready to receive messages
const readyState = connection.__driverState ?
CONN_DRIVERS[getConnDriverKindFromState(connection.__driverState)]
.getConnectionReadyState(...) : undefined;
if (readyState === ConnReadyState.OPEN) {
connection._sendMessage(toClientSerializer);
}
}Testing ConsiderationsSince there are no direct tests for
Security & Performance✅ No Security Concerns: The change is purely defensive and doesn't introduce any security issues. ✅ No Performance Impact: The readyState check is O(1) and happens before any serialization work, so it actually improves performance by short-circuiting unnecessary work. Code Quality✅ Follows CLAUDE.md Conventions:
ConclusionRecommendation: Approve with minor suggestions This is a solid defensive fix that addresses a real issue. The main suggestions are:
The PR is ready to merge as-is, but the suggestions above would make it even better. Great work catching this race condition! 🎯 |
Code Review - PR #3316OverviewThis PR adds a defensive check to prevent attempting to send messages over closed WebSocket connections, which was causing realtime functionality to break after disconnections. ✅ Positive Aspects
🔍 Observations & Suggestions1. Root Cause AnalysisThe comment states "this is likely a bug in RivetKit" - it would be valuable to understand when/why
2. Consistency with SSE DriverThe SSE driver doesn't have a similar check in its
3. Consider Checking Connection Status EarlierLooking at
Example: public _sendMessage(message: CachedSerializer<protocol.ToClient>) {
if (this.__driverState) {
const driverKind = getConnDriverKindFromState(this.__driverState);
const driver = CONN_DRIVERS[driverKind];
// Check connection state before sending
const readyState = driver.getConnectionReadyState?.(this.#actor, this, (this.__driverState as any)[driverKind]);
if (readyState !== ConnReadyState.OPEN) {
this.#actor.rLog.warn({
msg: "attempting to send message to non-open connection",
conn: this.id,
readyState,
});
return;
}
if (driver.sendMessage) {
// ... rest of logic
}
}
}4. Missing Test CoverageThere are no tests covering this scenario. Consider adding tests for:
🐛 Potential IssuesNone identified - the change is safe and defensive. 🎯 Performance ConsiderationsMinimal impact - adds one comparison operation before message sending, which is negligible. 🔒 Security ConcernsNone identified - this is a defensive fix that prevents errors. 📋 RecommendationsShort-term (this PR):
Follow-up considerations:
SummaryThis is a good defensive fix that prevents crashes when sending to closed WebSockets. The change is low-risk and improves reliability. However, it might be treating a symptom rather than the root cause - understanding why messages are being sent to closed connections would help determine if additional fixes are needed upstream. Recommendation: ✅ Approve and merge |
5d3cea0 to
d418443
Compare
8872a11 to
cc31979
Compare
Code Review for PR #3316SummaryThis PR adds a defensive check in the WebSocket driver to prevent errors when attempting to send messages to closed WebSocket connections. The fix addresses a bug where the realtime connection would break after a WebSocket disconnection. Positive Aspects✅ Correct Fix: The change properly checks Code Quality Observations1. Consistency with Other DriversThe SSE driver in this same file already has connection state checking in Recommendation: For consistency, consider whether the SSE driver should also have a similar guard in its 2. Potential Race ConditionThere's a subtle race condition: the WebSocket state could change between the Mitigation: The current approach is reasonable for most cases. If this becomes an issue, wrapping the entire send operation in a try-catch might be safer: try {
const serialized = message.serialize(state.encoding);
// ... existing send logic
state.websocket.send(data);
} catch (error) {
actor.rLog.warn({
msg: "failed to send message to websocket",
connId: conn.id,
error: error.message,
});
}3. Root Cause AnalysisThe warning message states "this is likely a bug in RivetKit" - this suggests there might be a deeper issue where messages are being queued or sent after disconnect. Questions to Consider:
Looking at the code, I notice that // In conn.ts _sendMessage method
public _sendMessage(message: CachedSerializer<protocol.ToClient>) {
if (this.__driverState) {
const driverKind = getConnDriverKindFromState(this.__driverState);
const driver = CONN_DRIVERS[driverKind];
// Check if connection is actually ready
const readyState = driver.getConnectionReadyState?.(this.#actor, this, (this.__driverState as any)[driverKind]);
if (readyState !== ConnReadyState.OPEN) {
// Log or silently skip
return;
}
if (driver.sendMessage) {
driver.sendMessage(/* ... */);
}
}
}This would be a more centralized fix that benefits all driver types. 4. Test CoverageNo tests were added for this bug fix. Recommendation: Consider adding a test case that:
Example test location: Security Considerations✅ No security concerns identified. The change is defensive and reduces error surface area. Performance Considerations✅ Minimal performance impact. The Breaking Changes✅ None. This is purely additive/defensive behavior. Final RecommendationAPPROVE ✅ This is a good defensive fix that addresses the immediate issue. The code quality is solid with proper structured logging. Optional follow-ups (not blockers):
Great work on the structured logging and minimal approach! |
PR Review: Fix WebSocket Error on Closed ConnectionSummaryThis PR adds a defensive check to prevent errors when attempting to send messages to closed WebSocket connections. The change is sound and addresses a real race condition issue. Code Quality ✅Strengths:
Minor suggestions:
Potential Issues 🔍Race condition handling: Recommendation: Consider wrapping the actual state.websocket.send() calls in a try-catch block to handle any exceptions that might occur if the connection closes between the readyState check and the send operation. Test Coverage 🧪Missing tests: I could not find any test files for connection drivers. This is a race condition fix that would benefit greatly from tests for sending a message after websocket closes, concurrent disconnect + send scenarios, and verifying the warning is properly logged. Consider adding integration tests in packages/rivetkit/src/driver-test-suite/tests/ following the pattern in raw-websocket.ts. Performance Considerations ✅The added check is minimal (single enum comparison) and will not impact performance. Early return on closed connections is actually more efficient than attempting to send. Security Concerns ✅No security concerns. The change is purely defensive and does not expose any new surfaces. Final Verdict 🎯Approve with suggestions The fix is correct and addresses the issue described in the PR title. The code follows the repository patterns and conventions. My suggestions are minor improvements rather than blockers. The PR is good to merge as-is, but the above improvements would make the codebase more robust. |
Merge activity
|
…altime to break after a connection disconnects (#3316)
…altime to break after a connection disconnects
cc31979 to
43ed336
Compare
d418443 to
74873cd
Compare
|
Deployment failed with the following error: Learn More: https://vercel.com/docs/limits#rate-limits |
Code ReviewThank you for this fix! The change addresses a race condition where messages could be sent to a closed WebSocket connection. Here's my detailed review: ✅ Positive Aspects
🔍 Suggestions for Improvement
📋 Additional Observations
🎯 RecommendationApprove with minor suggestions. The fix is solid and addresses the root cause. The suggestions above are nice-to-haves that could be addressed in follow-up PRs if desired. The change correctly prevents the error that was breaking realtime functionality when WebSockets disconnect, which is the primary goal. |
Code ReviewSummaryThis PR adds a guard to prevent sending messages to closed WebSocket connections, which was causing RivetKit's realtime functionality to break after connection disconnects. The fix is straightforward and addresses a real bug. Positive Changes ✅
Suggestions for Improvement1. Consider consistency with SSE driverThe SSE driver doesn't have a similar guard in its Current SSE driver (lines 182-190): sendMessage: (
_actor: AnyActorInstance,
_conn: AnyConn,
state: ConnDriverSseState,
message: CachedSerializer<protocol.ToClient>,
) => {
state.stream.writeSSE({
data: encodeDataToString(message.serialize(state.encoding)),
});
},Suggestion: Consider adding a similar check: if (state.stream.aborted || state.stream.closed) {
actor.rLog.warn({
msg: "attempting to send message to closed SSE stream",
connId: conn.id,
});
return;
}2. Log message clarityThe log message says "this is likely a bug in RivetKit" - while this might be true, it could cause confusion for users. Consider:
3. Test coverageThis appears to be a bug fix for a race condition. Consider adding a test that:
This would prevent regression and document the expected behavior. Minor Observations
Security & Performance
Overall AssessmentThis is a solid defensive fix that prevents a real issue. The implementation is clean and follows existing patterns in the codebase. The main opportunities for improvement are:
Recommendation: Approve with suggestions - The fix is good to merge as-is, but the suggestions above would make it even more robust. |
…altime to break after a connection disconnects (#3316)

No description provided.