Quick answer: Fetch the bus via GetNode<EventBus>("/root/EventBus") inside _Ready, not in field initializers. Rebuild the C# project after editing autoload setup.

An event bus autoload coordinates score/health updates across scenes. A C# enemy class calls bus.EmitScored(10) on death — NullReferenceException. Bus reference was captured before the autoload finished loading.

The Bug Pattern

public partial class Enemy : Node3D
{
    private EventBus _bus = (EventBus)Godot.Engine.GetMainLoop();   // runs too early

    public override void _Ready()
    {
        _bus.EmitScored(10);   // _bus is null
    }
}

Field initializers run during construction, before scene tree finishes setup. Autoload may not be ready.

Correct Lookup

private EventBus _bus;

public override void _Ready()
{
    _bus = GetNode<EventBus>("/root/EventBus");
    _bus.EmitScored(10);
}

By _Ready, autoload nodes are in /root and accessible. GetNode resolves the path.

Lazy Property

private EventBus _bus;
private EventBus Bus => _bus ??= GetNode<EventBus>("/root/EventBus");

First access fetches the bus, subsequent uses return cached. Works in any callback after the node enters the tree.

Cross-Autoload Order

Autoloads load in the order listed in Project Settings → Autoload. If AutoloadA._Ready references AutoloadB, B must be above A. Otherwise defer:

public override void _Ready()
{
    CallDeferred(nameof(WireBus));
}

private void WireBus()
{
    var bus = GetNode<EventBus>("/root/EventBus");
    bus.Scored += OnScored;
}

Rebuild After Autoload Edit

Adding/renaming autoloads triggers a project.godot change. C# generated wrappers may need a rebuild: Build menu → Build Solution. Stale wrappers = null at runtime.

Verifying

Spawn enemy, kill it, verify Scored signal fires and listeners react. No null refs across scene reloads. Stress test: spawn 1000 enemies and kill them all; no missed signals.

“Autoload references belong in _Ready, not constructors. Lazy properties or explicit GetNode keep cross-autoload code clean.”

For larger projects, generate a strongly-typed Globals.cs that caches each autoload by GetNode call — one place to update if you rename, one type-safe accessor per autoload.