Skip to content

FOS Sync Doctor

Version 1.2.1 · requires FOS Essentials Core 1.1.0 or later

Audits the synchronised behaviours of your world for the mistakes that never raise an error — the ones you only discover when a player joins your instance and sees a different world from everyone else.

Free. Requires FOS Essentials Core 1.1.0.

Menu: FOS Essentials > FOS Sync Doctor > Audit.


This pack contains editor code only. No component, no prefab, no behaviour. It reads your scripts and the components in your scene, and reports; it never runs in game and costs nothing at runtime.

That is also why it ships without a Runtime assembly or a Prefabs folder, unlike every other FOS pack: there would be nothing to put in them.


A sync mode that contradicts the script — error

Section titled “A sync mode that contradicts the script — error”

The mode a script declares is not what runs. What runs is the value stored on the UdonBehaviour in your scene, and the two drift apart the moment a component is replaced, duplicated or pasted back. Nothing warns you: the inspector goes on showing the declared mode.

When manual sync ends up stored as continuous, the payload goes over the ~200 byte continuous cap, the encoder fails, VRChat retries it forever, and nothing leaves the owner’s machine any more. Everything still works locally, so the world simply looks dead to everyone else — with no error to explain it.

This is the one finding the window can repair. Fix puts the declared mode back; save the scene afterwards or the correction is lost.

Synchronised variables with no OnDeserialization — error

Section titled “Synchronised variables with no OnDeserialization — error”

The single most common late-joiner bug. The values arrive, and nothing applies them. For the player who joined, the world stays in its initial state — permanently.

A field carrying [FieldChangeCallback] is not reported: the callback fires on deserialisation, so that pattern is already correct.

RequestSerialization() inside Start() — warning

Section titled “RequestSerialization() inside Start() — warning”

VRChat documents that a serialisation requested from Start() does not reach players who join later. Send it from an event, or from OnPlayerJoined.

Manual sync that never serialises — warning

Section titled “Manual sync that never serialises — warning”

Manual sync sends nothing until you ask. Without a single RequestSerialization() the variables are, in practice, local.

The Core’s helpers count as a serialisation: a script calling FOSSync() or FOSTakeOwnershipAndSync() is not reported, since both request one on your behalf.

Serialising with no ownership check — warning

Section titled “Serialising with no ownership check — warning”

Only the owner can serialise. Called by anyone else, RequestSerialization() silently does nothing — the most confusing failure in Udon, because everything looks fine locally.

Continuous sync carrying an array or a string — warning

Section titled “Continuous sync carrying an array or a string — warning”

Continuous sync is capped at roughly 200 bytes per behaviour. A variable-length field goes over it without warning. Manual sync carries far more.

Several behaviours on one object, disagreeing on the mode — error

Section titled “Several behaviours on one object, disagreeing on the mode — error”

VRChat applies one sync mode per GameObject. Put two Udon behaviours on the same object and the others inherit the mode that wins, whatever their own script declares — a display set to NoVariableSync sitting next to an authority set to Manual quietly degrades that authority.

This is the cause behind the finding above. Without it you repair the stored mode, save, and it comes back at the next recompile, with nothing to say where it came back from.

The fix is never a value to change: move the behaviour that synchronises onto its own GameObject.

Without [UdonBehaviourSyncMode] the mode defaults to Any and stays changeable in the inspector, where it can be altered by accident.

A broadcast event acting on the local player — warning

Section titled “A broadcast event acting on the local player — warning”

The one that costs the most time to understand. A network event sent to every client runs on every client, and Networking.LocalPlayer is a different player on each of them. A teleport meant for one person moves the whole instance.

Player events used without isLocal — warning

Section titled “Player events used without isLocal — warning”

OnPlayerTriggerEnter and its siblings fire on every client, for every player entering. Without the check, a stranger walking through your volume triggers the effect on your machine.

A network event whose name starts with _ — warning

Section titled “A network event whose name starts with _ — warning”

VRChat refuses to run such an event remotely. The call leaves, nothing happens, and nothing is logged.


Findings come in two kinds, and the window never mixes them up.

Certain findings are established by inspecting the compiled types: which fields carry [UdonSynced], which sync mode is declared, whether OnDeserialization is overridden. These are facts.

Probable findings are deduced from the text of your scripts, because the body of a method cannot be inspected any other way. “This script never calls RequestSerialization” is wrong if another script calls it for you. Those findings carry a probable tag and are worth a look rather than a blind fix.

Comments and string literals are stripped before anything is searched, so a script explaining “no ownership check here” in a comment is not mistaken for one that performs the check.

An audit tool that cannot tell you how much to trust it is not worth much.


  • It reads your scripts, and one thing in your scene: the sync mode stored on each component. Wiring is outside what it can see — a behaviour that is perfectly written but pointed at the wrong object still looks fine to it.
  • It does not check performance, lighting or Quest budgets. VRWorldToolkit does that well, and for free.
  • It changes nothing on its own. Every finding is a report, and the one Fix button — on a contradictory sync mode — only acts when you click it.

By default the FOS packs themselves are skipped — you are auditing your world, not this line. The toggle exists if you want to look.