Skip to content

fix(rivetkit): bind methods through createWriteThroughProxy#4987

Open
abcxff wants to merge 1 commit into
05-06-fix_log_error_on_failed_inspector_requestsfrom
05-06-fix_rivetkit_bind_methods_through_createwritethroughproxy
Open

fix(rivetkit): bind methods through createWriteThroughProxy#4987
abcxff wants to merge 1 commit into
05-06-fix_log_error_on_failed_inspector_requestsfrom
05-06-fix_rivetkit_bind_methods_through_createwritethroughproxy

Conversation

@abcxff
Copy link
Copy Markdown
Contributor

@abcxff abcxff commented May 6, 2026

Fixes an error that occurs when inspector state attempts to serialize non trivial values (ie Date).

@railway-app
Copy link
Copy Markdown

railway-app Bot commented May 6, 2026

🚅 Deployed to the rivet-pr-4987 environment in rivet-frontend

Service Status Web Updated (UTC)
kitchen-sink ❌ Build Failed (View Logs) Web May 11, 2026 at 3:42 am
website 😴 Sleeping (View Logs) Web May 7, 2026 at 2:22 am
frontend-inspector ❌ Build Failed (View Logs) Web May 6, 2026 at 10:05 pm
frontend-cloud ❌ Build Failed (View Logs) Web May 6, 2026 at 10:04 pm
ladle ❌ Build Failed (View Logs) Web May 6, 2026 at 8:30 pm
mcp-hub ✅ Success (View Logs) Web May 6, 2026 at 8:30 pm

Copy link
Copy Markdown
Contributor Author

abcxff commented May 6, 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
Contributor

claude Bot commented May 6, 2026

Code Review: fix(rivetkit): bind methods through createWriteThroughProxy

Overview

This 3-line change adds method binding inside the get trap of createWriteThroughProxy so that functions retrieved from a proxied state object receive the correct this (the raw inner target). The stated motivation is to fix serialization errors when inspector state contains non-trivial values like Date.


What the fix solves

Without this change, calling prototype methods on built-in types stored in actor state throws a TypeError at runtime because those types use internal slots that a Proxy cannot satisfy. For example, date.toISOString() requires this to have a [[DateValue]] internal slot, which the proxy wrapper cannot expose. Binding to innerTarget makes those calls work correctly.


Critical issue: mutation tracking silently broken for array/object methods

The binding is applied unconditionally to every function, including methods on plain arrays and objects. This breaks write-through mutation tracking.

Before this PR: arr.push(x) calls push with this = proxy, which fires the set trap (push sets this[length]), which calls commit(). Mutation is tracked.

After this PR: result.bind(innerTarget) means push runs with this = raw array, bypassing the set trap entirely. commit() is never called. Mutation is silently dropped.

The same regression applies to pop, splice, sort, reverse, and any other method that mutates via index assignment. Actor state modified through these methods will silently not persist.


Suggested fix

Only bind to innerTarget for built-in types whose methods require an internal slot. For plain objects and arrays, keep the proxy as the receiver so mutations still flow through the set trap:

get(innerTarget, property, receiver) {
    const result = Reflect.get(innerTarget, property, receiver);
    if (typeof result === "function") {
        const internalSlotTypes = [Date, Map, Set, WeakMap, WeakSet, RegExp, ArrayBuffer, DataView];
        if (internalSlotTypes.some(T => innerTarget instanceof T)) {
            return result.bind(innerTarget);
        }
        return result.bind(proxy);
    }
    return result && typeof result === "object"
        ? wrap(result as object)
        : result;
},

(proxy can be forward-referenced with a let proxy: object declaration before the handler literal, then assigned after new Proxy(...). The same pattern is already used for proxies.set(target, proxy).)


Additional concerns

  • No tests. Two behaviors need coverage: (a) Date/Map/Set method calls through the proxy work without throwing, and (b) Array.push / splice through the proxy still triggers commit. The driver test suite (rivetkit-typescript/packages/rivetkit/tests/driver/) is the right home.
  • Draft state / unfilled template. The PR description still has the template checklist and no root-cause context. Please fill it in before requesting review.
  • Comment the invariant. A short inline comment explaining that internal-slot types require binding to the raw target would help future readers avoid regressing this again.

Summary

The intent is correct. Bound receivers are required for built-in types with internal slots. But binding unconditionally to all functions silently disables write-through mutation tracking for arrays and objects, which is the core purpose of this proxy. Please scope the binding to known internal-slot types, add test coverage for both the fixed case and the mutation-tracking case, and fill in the PR description before merging.

@abcxff abcxff force-pushed the 05-06-fix_log_error_on_failed_inspector_requests branch from b150344 to fc4756e Compare May 6, 2026 22:03
@abcxff abcxff force-pushed the 05-06-fix_rivetkit_bind_methods_through_createwritethroughproxy branch from af6e0e4 to 3be897d Compare May 6, 2026 22:03
@abcxff abcxff force-pushed the 05-06-fix_rivetkit_bind_methods_through_createwritethroughproxy branch from 3be897d to c39972c Compare May 7, 2026 20:16
@abcxff abcxff force-pushed the 05-06-fix_log_error_on_failed_inspector_requests branch from fc4756e to 5ec0839 Compare May 7, 2026 20:16
@abcxff abcxff marked this pull request as ready for review May 7, 2026 20:19
@abcxff abcxff requested a review from NathanFlurry May 7, 2026 20:23
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