Troubleshooting

FAQ

Direct answers for duplicate handlers, token cleanup, lambdas, priority slot order, scheduled state, tracking, and threading.

  • Duplicate handlers
  • Cleanup ownership
  • Tracking visibility
  • Threading defaults

Direct answers for the setup and lifecycle issues new users usually encounter first.

Deep references:

Why Is My Handler Running Twice?

The same owner probably subscribed twice without disposing the first token.

Use one bag per lifecycle and clear it at the matching boundary:

using UnityEngine;
using Azkar.Eda;

public sealed class SafeListener : MonoBehaviour
{
    private readonly AzEventTokenBag _tokens = new();

    private void OnEnable()
    {
        GameEvents.Started.Subscribe(OnStarted).AddTo(_tokens);
    }

    private void OnDisable()
    {
        _tokens.DisposeAll();
    }

    private void OnStarted() { }
}

What Happens If I Forget Token Cleanup?

The subscription can stay connected longer than the object that created it. In Unity, that often appears as duplicate callbacks, callbacks to disabled objects, or callbacks after a scene unload.

The fix is simple: every subscription token needs an owner.

Can I Remove A Lambda Later?

With C# events, removing a lambda is awkward because you need the same delegate instance. With Azkar EDA, keep the token or add it to a bag.

using UnityEngine;
using Azkar.Eda;

public sealed class LambdaExample : MonoBehaviour
{
    private readonly AzEventTokenBag _tokens = new();

    private void OnEnable()
    {
        GameEvents.Started
            .Subscribe(() => Debug.Log("Started"))
            .AddTo(_tokens);
    }

    private void OnDisable()
    {
        _tokens.DisposeAll();
    }
}

What If Two Priority Subscribers Use The Same Slot?

Same-slot subscribers have the same importance level; they do not have a guaranteed order within that slot. If two independent handlers both use slot 5, either one may run first and the code should still be correct.

If one handler depends on another, make the order explicit with separate named slots instead of relying on subscription order or raw numbers.

public static class SavePriority
{
    public const byte Validate = 1;
    public const byte Write = 5;
    public const byte Notify = 9;
}

SaveBus.Saving.Subscribe(ValidateSave, SavePriority.Validate);
SaveBus.Saving.Subscribe(WriteSaveFile, SavePriority.Write);
SaveBus.Saving.Subscribe(NotifyPlayer, SavePriority.Notify);

Assuming lower slots run first, the order is now validation, writing, then notification. The names also make intent clearer, and the gaps leave room to add a later step such as Prepare = 3.

Why Did My Scheduled State Not Notify Immediately?

State writes may be scheduled or coalesced depending on the state API you used. A scheduled write is not always the same as "notify all subscribers on this exact line."

Check:

  • Did you use SourceSet or a scheduling helper?
  • Are multiple writes being coalesced?
  • Are you observing before the scheduled notification has run?
  • Does the new value equal the old value?

See Scheduling And Validation for state scheduling details.

How Should I Handle Two-Way Binding?

Keep one clear source of truth and reconcile changes carefully. A UI control and a model can both produce changes, but one side should own the final accepted value.

Common pattern:

  • UI proposes a value.
  • Model validates or clamps it.
  • AzState<T> publishes the accepted value.
  • UI updates from the accepted state.

This prevents endless loops and lets validation stay in one place.

Why Is Tracking Not Showing My Item?

Check these first:

  • Is the item annotated with [AzEdaTrack]?
  • If it is instance-owned, did you call AzEdaTracking.Register?
  • Is the object active in Play Mode?
  • Did the subscription path actually run?
  • Did you open Window/Azkar EDA/Tracking and refresh or start tracking?
  • Are filters hiding the group, tag, or role?

Why Are Captured Values Missing?

Value samples require CaptureValues and a supported event payload or state value.

using Azkar.Eda;

public static class DebugBus
{
    [AzEdaTrack("Damage", Role = AzEdaTrackRole.Event, CaptureValues = true)]
    public static readonly AzEvent<int> Damage = new();
}

Also confirm the event or state actually changed after tracking started.

Can I Invoke Events Off Thread?

Be conservative. Unity object work should happen on the main thread. Worker code should produce plain data, queue it, and let a main-thread bridge invoke events or update state.

using System.Collections.Concurrent;
using UnityEngine;
using Azkar.Eda;

public sealed class WorkerBridge : MonoBehaviour
{
    private readonly ConcurrentQueue<int> _pending = new();

    public void EnqueueFromWorker(int value)
    {
        _pending.Enqueue(value);
    }

    private void Update()
    {
        while (_pending.TryDequeue(out int value))
        {
            GameEvents.ScoreChanged.Invoke(value);
        }
    }
}

Should I Use InvokeFast Everywhere?

No. Use Invoke first. InvokeFast is for measured AzPriorityEvent hot paths with trusted subscribers. Use InvokeSafe when one failing listener should not stop the others.

Why Does Nothing Happen When There Are No Subscribers?

That is normal. Events notify current subscribers. If an event represents a value someone may need later, use AzState<T> instead.

Should I Dispose Or Pause A Token?

Dispose when the owner no longer needs the subscription. Pause or resume only when the same owner still owns the subscription but wants temporary silence.

For normal Unity OnEnable and OnDisable, disposing the bag is usually the cleanest pattern.