Skip to content

v1.9.0 — Replicated state that always arrives

Back to all updates
v1.9.0September 24, 2026

Replicated state of any size reaches the client, and player data survives migrations and departures.

Four changes can reach existing code.

  • Migrations no longer run for a player with no saved data. If a step also set up a value that new players need, put that value in the Template.
  • Remote:Connect, Remote:Once and Remote:Wait throw on the server, where they never received anything. Serve the remote with Net.Handle.
  • A whole number token given a fraction throws. Round the value first, or declare a fractional token such as NumberF32.
  • A Navigation agent can fail with a new reason, faulted.

A player’s first view of Replication used to travel as one call holding every key. That call used Types.Any, which stops at 4096 parts. An inventory of a thousand items with three fields each is past that, so the call was dropped. The client never received its first view, and every later change for that player was ignored for the rest of the session.

Each key now travels as its own call. A key that cannot be sent holds back only itself, is reported, and goes again the next time it is written. Types.Any also accepts wider bounds for what the server sends:

CeilingClient to serverServer to client
Depth1664
Parts in one call40961048576
Entries in one table409665535

The playtest that proved the fix sent 1500 items to a client, then a change to one of them. Both arrived.

Player data survives migrations and departures

Section titled “Player data survives migrations and departures”

Three faults in Data could damage what a player owns.

  • New players ran every migration. The template was treated as data saved at version 1, so a step that read a field only old data has failed, and the new player was kicked. Migrations now run only for data saved at an older version.
  • A failed migration was saved. A step that raised left behind what it had already converted, and the session saved it on the way out. Steps now run on a copy, which replaces the live data only when every step succeeds.
  • Data could disappear before services finished. Data was released on its own PlayerRemoving connection, which could run before a service’s OnPlayerRemoving. Release now happens when the player’s bag closes, after every service has had its turn.

Monetization answered a redelivered receipt as granted whenever the grant was recorded in memory, even when the first save had not been confirmed. A receipt seen before is now answered the same way as a new one: PurchaseGranted only once the save recording it is confirmed.

  • A weighted Chance draw from a committed round now lands on the same entry on every server, because entries are weighed in the order they were added.
  • Store retries a last save that failed, and shutdown no longer counts a save still in flight as finished.
  • Loop.After releases its entry once it runs, so the framework bag no longer grows with every call that names no owner.
  • Schema no longer raises on a bound that is not a number.
  • A Watch callback that raises no longer stops the members after it from being announced.

Every change in this release is listed in the changelog.