Notification rules

What happens when a subscriber subscribes, unsubscribes or writes back while being notified.

Updated 2026-09-08

Most of the time a subscriber reads the value and updates something. This page is about the rest: what happens when a subscriber changes the subscriber list, or the value, from inside its own callback.

A subscriber must not write back to its own observable

This does not compile:

score.Subscribe(Clamp);

private void Clamp(int value)
{
    if (value > 10)
        score.Value = 10;      // UDN0012
}

Udonite refuses it. A subscriber reaching its own observable again closes a cycle through a delegate, and Udon cannot spill a frame for an indirect call, so the program it would emit does not return correctly.

The refusal is the good outcome. The shape it catches includes the quiet one, where the hop back is a property setter rather than a method. A world that runs it does not fail fast: it freezes until VRChat kills the behaviour with Program execution time exceeded max VM time of 10,0 seconds, which is ten seconds of frozen world and no useful message.

Clamp before assigning instead:

score.Value = raw > 10 ? 10 : raw;

Or write to a different observable, which is an ordinary thing to do.

Unsubscribing during a notification takes effect from the next one

A subscriber may unsubscribe itself, or anyone else, from inside its callback. The notification in flight still finishes with the subscribers it started with, and the removal applies to every notification after it.

private void Once(int value)
{
    score.Unsubscribe(Once);      // fine: the rest still get called,
}                                 // and Once is not called again

This is what a C# event does. Removing a subscriber builds a new list rather than editing the one being walked, which is the same reason unsubscribing from inside a handler is safe at all.

So a behaviour being torn down may still be called once more, in the round that is already running. If a handler must not run after its object is gone, guard the handler rather than relying on the removal:

private void OnScoreChanged(int value)
{
    if (closing)
        return;

    label.text = value.ToString();
}

Subscribing during a notification waits for the next one

A handler added while a notification is in flight is not called by that notification. It has not missed anything it was present for, and calling it inside the round it just joined would be a surprise.

Subscribing twice does nothing the second time

The usual way to end up subscribed twice is a Start that runs again. Being called twice per change is a bug every time, so the second subscribe is ignored rather than honoured.

Notifying everybody, and notifying one

Publish() notifies every subscriber, whether or not anything changed.

To bring a single late subscriber up to date, pass true to Subscribe instead. Using Publish for that works, but it also redraws every other subscriber for no reason, and miscounts for any of them keeping a tally.

score.Subscribe(OnScoreChanged, true);   // this handler only
score.Publish();                          // all of them

Reading is always silent

Count, Contains, IndexOf, ToArray, ContainsKey, TryGetValue and every getter notify nobody. Only a change does.

SetSilently and AddSilently change without notifying, for seeding a starting value that subscribers have no business reacting to, such as a score restored on join.

Something went wrong Reload