Quick answer: Name delegate with EventHandler suffix. Emit via EmitSignal(SignalName.HealthChanged, value). Connect via C# event syntax.

You write EmitSignal("health_changed", 50) by hand. Method connected to HealthChanged never runs because the literal name doesn’t match the generated signal.

The Symptom

EmitSignal returns; receiver method does not. No error.

The Fix

public partial class Player : Node {
    [Signal]
    public delegate void HealthChangedEventHandler(int newHealth);

    public void TakeDamage(int amount) {
        Health -= amount;
        EmitSignal(SignalName.HealthChanged, Health);
    }
}

public partial class UI : Control {
    [Export] public Player Player;
    public override void _Ready() {
        Player.HealthChanged += OnHealthChanged;
    }
    private void OnHealthChanged(int hp) {
        bar.Value = hp;
    }
}

Source generator strips EventHandler from delegate name to produce signal HealthChanged. SignalName.HealthChanged is the type-safe constant used at emit time.

Verifying

Take damage. UI bar updates. Without the EventHandler suffix: signal isn’t generated, EmitSignal silently fails.

“EventHandler suffix. SignalName constant. Connection holds.”

Related Issues

For C# Export flags, see flags. For C# List property, see List.

Suffix matters. Constants safe. Signal lands.