Quick answer: change_scene_to_file frees the current tree. Any signal connections from autoloads to nodes inside that tree become stale. Reconnect signals in _ready of the new scene’s nodes, and disconnect them in _exit_tree. For ergonomics, connect with CONNECT_REFERENCE_COUNTED.

Here is how to fix Godot signal connections that disappear after a scene change. Your main menu connects an autoload event to a UI label. The user clicks Play, the scene swaps, and a few seconds later you get Attempt to call function 'on_score_changed' in base 'previously freed object'. Or worse, no error — the signal just stops updating the UI because the receiver no longer exists.

The Symptom

You connect a signal once in _ready and it works. After calling get_tree().change_scene_to_file, signals from autoloads no longer reach handlers in the new scene. Or signals continue firing but produce errors mentioning “previously freed object”. Reloading the same scene with get_tree().reload_current_scene shows the same problem.

What Causes This

Receivers freed without disconnect. When a scene changes, all its nodes are freed. Their signal connections to long-lived emitters (autoloads, parent nodes) remain in the emitter’s list. The next emit walks those records and calls into freed memory.

New scene does not reconnect. The new scene’s nodes do not automatically inherit the previous scene’s signal connections. Each new _ready must establish its own.

Duplicate connections on reload. If you connect in _ready without checking is_connected, repeated reloads can stack multiple connections, causing handlers to fire multiple times per emit.

Lambda captures. A lambda passed to connect can hold references to local variables. When those references go stale, lambda invocations fail unpredictably.

The Fix

Step 1: Connect in _ready and disconnect in _exit_tree.

extends Label

func _ready():
    GameState.score_changed.connect(_on_score_changed)
    _on_score_changed(GameState.score)

func _exit_tree():
    if GameState.score_changed.is_connected(_on_score_changed):
        GameState.score_changed.disconnect(_on_score_changed)

func _on_score_changed(value: int):
    text = "Score: %d" % value

This guarantees clean teardown when the scene unloads, leaving the autoload with no stale records.

Step 2: Use CONNECT_REFERENCE_COUNTED for automatic cleanup.

func _ready():
    GameState.score_changed.connect(
        _on_score_changed,
        CONNECT_REFERENCE_COUNTED
    )

Reference-counted connections automatically disconnect when the receiver object is freed. Combined with is_connected guards, this prevents stacking on reload.

Step 3: Guard against duplicate connections.

func _ready():
    if not GameState.score_changed.is_connected(_on_score_changed):
        GameState.score_changed.connect(_on_score_changed)

Step 4: Avoid lambdas for long-lived connections. Use named methods so you can disconnect them precisely.

# Avoid - lambdas are hard to disconnect
GameState.score_changed.connect(func(v): label.text = str(v))

# Prefer - named method
GameState.score_changed.connect(_on_score_changed)

Step 5: For nodes nested in scenes, prefer signals from immediate parents. Connecting from a leaf node directly to an autoload is fragile. Have the leaf emit its own signal, the parent re-emit, and the autoload subscribe to the parent. Lifetime management becomes simpler.

Detecting Stale Connections

Add a one-shot debug print in your autoload right before each emit:

func change_score(value: int):
    score = value
    for conn in score_changed.get_connections():
        if not is_instance_valid(conn["callable"].get_object()):
            push_warning("Stale connection on score_changed")
    score_changed.emit(value)

If you see warnings, you have an unclean disconnect somewhere. Add _exit_tree handlers to the offending receivers.

“Connect in _ready, disconnect in _exit_tree, or use CONNECT_REFERENCE_COUNTED. Pick a strategy and apply it everywhere.”

Related Issues

For autoload visibility issues, see Autoload Not Accessible. For preload null returns, see Preload Not Finding Nested Resource.

Connect in ready. Disconnect in exit. Or let reference counting do it for you.