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.
Nothing is added to your world
Section titled “Nothing is added to your world”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.
What it checks
Section titled “What it checks”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.
No sync mode declared — note
Section titled “No sync mode declared — note”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.
Certain, and probable
Section titled “Certain, and probable”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.
What it does not do
Section titled “What it does not do”- 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.
