You are building a factory with friends, nothing lags, and then one player is kicked out and told to re-download the whole map. The connection was fine. In Factorio, that is the signature of a desync, and it is almost never a network problem.
At a glance
Factorio, a factory-building game by Wube Software, running on a custom deterministic engine, not Unity or Godot.
The model: deterministic lockstep. Every player simulates the whole game locally, and only player actions travel over the network, not world state.
The definition: any tiny difference in simulation between two players is a desync, forcing a reconnect and a full map re-download.
The two deep cases, both from Factorio Friday Facts 340: a Serpent placeholder value, and a unit-group speed value that was never saved.
The counterintuitive rule: network, latency and performance problems do not cause desyncs. Slowness and desync are different problems with different fixes.
Sources: Factorio Friday Facts 76, 188, 63, 70, 242 and 340, all on factorio.com.
Last verified: October 27, 2026.
Not lag: every player replays the same game, and one replay drifted
Factorio's multiplayer model, explained in Friday Facts 76, is the opposite of what most players expect. There is no server sending everyone the current world. Every peer runs the full simulation of everything happening in the world, and when a player acts, only the action is sent to the others. Bandwidth stays tiny, because a keyboard can only produce a few hundred bytes per second, and a map with hundreds of objects feels the same as one with millions. The price is that small errors cannot be corrected. They accumulate until two players are seeing different games, and that is what Factorio players call a desync. The same post names the other cost: game speed is limited by the slowest player, because a frame cannot finish until every other node's input has been processed, and a latency buffer that eases this also delays local actions. Slowness, in other words, is built into the model. Desync is not slowness. It is drift.
Friday Facts 188, written when the 0.15 experimental release needed desync help from players, gives the working definition. All players must simulate every tick identically, and any computer that differs even slightly causes a desync, forcing that player to reconnect and re-download the map. The post also gives the reporting discipline that makes these bugs fixable: a desync report holds server and client state for comparison, and it helps most when it comes with what the player was doing and a reproduction recipe, such as load this save, wait for a friend to join, click this inserter, desync. A report file alone, without context, is a weak lead.
Deep case one: a placeholder that meant delete
Friday Facts 340 investigates two mod-related desync reports from a week when players were reporting more than usual, shortly after the Krastorio 2 mod release. The first case starts with a reasonable design. In a Construction Drones style mod, a player records the drones they own, and each drone records which player owns it. At runtime, mutual references are no problem. At save time, the Serpent serializer walks all data in global and handles cycles by writing a placeholder for an object it has already seen, then coming back to fix it up. The problem is that Serpent chose nil as the placeholder, and in Lua, setting a table value to nil deletes the key. While the placeholder was in place, traversal could not see the key at all, and when Serpent came back to fix it, each peer ended up with a different table ordering, because of deeper details in Factorio's customized Lua. The fix, by Boskid, was one word long in effect: change the placeholder from nil to 0, so iteration order survives saving and loading. The post calls the problem deep and hard to find, and the fix simple. Both halves matter. A one-value fix does not mean a shallow bug.
Deep case two: a cached speed that was never saved
The second report came from a player running Krastorio 2 with the Rampant mod, and on the surface it looked like Rampant's fault, because the differences were all in unit positions and Rampant manages units. The real cause was quieter. A unit group caches a maximum speed value, set by its slowest unit, but that value was not saved. It was recalculated on every load. That had always been true, but before 0.17 a unit's maximum speed was decided by its prototype alone, so the recalculated value never changed. Then 0.17 added two mod capabilities, terrain-affected speeds and script-changed speeds, and the base game itself does not use either, so the problem stayed hidden until a mod did. On Krastorio 2 creep terrain, which penalizes unit speed, the group recalculated a lower maximum speed after loading, and the two simulations diverged. The fix was to make units notify the group to recalculate when necessary, instead of trusting a value that load had silently changed. The pattern behind both cases is one sentence: the bug is rarely in the logic you can see moving. It is in a value or an ordering you assumed load would reproduce exactly.
The older pattern: hidden state, hash order, and invariants
The two 340 cases sit on an older pattern the Friday Facts return to again and again. Friday Facts 63 says most desyncs come from hidden state that was not properly initialized or saved, and describes testing a suspect save in a heavy mode that hunts exactly that class of bug. This article names the idea only, because the post gives no command or step list to copy. Friday Facts 70 reports a fix deep inside Factorio's Lua implementation, after mods iterating hash-table values in an uncertain order caused desyncs. Friday Facts 242 adds invariants: stacked walls must never exist, because neighboring walls could then connect to different walls on different players and desync, and belts, pipes and rails have similar invariants. Factorio answers that with a consistency check that walks the whole map on version transitions. The through-line for a solo developer is not any of Factorio's tools, which belong to Factorio alone. It is the checklist: saved values, fixed order, placeholder semantics, change notifications, and stated invariants that get checked instead of assumed.
What to do Monday morning
First, classify the symptom: slowness, packet loss and low FPS are not desync. If players are kicked and must re-download the map, suspect determinism.
Get a reproducible save and the exact player actions before the desync, the way Friday Facts 188 asks for reports.
Treat load as the first suspect interval: compare before-load and after-load state, and hunt values that are recalculated instead of saved.
Fix iteration order everywhere it matters, and check what your placeholder value means in your language: a placeholder that equals delete is a desync waiting to happen.
When a value changes, notify everything that cached it, instead of letting dependents keep a stale copy.
Write down one invariant your game must never violate, and check it on load or migration, before players find the violation in multiplayer.

