Skip to content

Remove deleted app from memory - #2653

Open
petersohn wants to merge 5 commits into
AppDaemon:devfrom
petersohn:fix-unload-bug
Open

petersohn wants to merge 5 commits into
AppDaemon:devfrom
petersohn:fix-unload-bug

Conversation

@petersohn

@petersohn petersohn commented Jul 11, 2026 •

Copy link
Copy Markdown

Fix 1: When an app is removed, completely remove it from memory.

  • Remove from app_config.root. If left here stale, the next config update will restart the deleted app.
  • Remove from objects. This is needed because otherwise we get an AssertError because of the unmatched thread count.

Possible side effect: When restarting an app because of a config update, it's completely unloaded from memory and then recreated, resulting in a slight performance overhead.

Fixes #2508.

Fix 2: Keep apps stopped while a plugin is down

When a plugin (e.g. HASS) is restarted, the apps would start before the plugin is up, potentially causing errors. If an app fails inside initialize(), it will be in a broken state, and only an appdaemon restart fixes that.

The fix is to wait until all plugins are up before starting apps.

Possible side effect: if there are multiple plugins, apps will only start once all of them are up.

When a plugin (e.g. HASS) fails, PLUGIN_FAILED stops and deletes all of
its apps' ManagedObjects. The utility loop's periodic NORMAL
check_app_updates() pass then sees every app as "not in objects" and
re-adds them to init, prematurely restarting all apps against the dead
plugin (spurious restart wave; apps crash in initialize with connection
errors). The apps should stay stopped until the plugin reconnects and
PLUGIN_RESTART runs.

- Add PluginManagement.any_plugin_inactive() and skip the not-in-objects
  init repopulation in check_app_config_files while any plugin is marked
  inactive. The guard covers only that repopulation; config-file-change
  loads are intentionally not guarded.
- Record the stopped app names per namespace when a plugin fails
  (AppManagement.stopped_plugin_apps) and restore them in
  _start_plugin_apps on PLUGIN_RESTART, since the deleted ManagedObjects
  no longer carry the namespace. Snapshot entries are filtered at pop
  time to configs that still exist and are not disabled.
- Register plugins as active from the start via a _register_plugin()
  seam (also seeds last_plugin_state). The active flag is now purely a
  runtime failure signal: a never-connected plugin no longer counts as
  inactive, so the initial app load can't hit the guard at boot (with
  production_mode the utility loop never re-runs check_app_updates, so
  a boot-time guard hit would leave no apps running).
- Make HassPlugin.stop() tolerate a never-connected plugin (ws/session
  are None until a websocket connect succeeds) and wrap per-plugin stop
  calls in PluginManagement.stop() so one failing plugin cannot abort
  the whole shutdown sequence.

Fixes the HASS-restart app storm (wave 1: 126 apps spuriously started,
27 AppStartFailures, double restart per HASS restart).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improper cleanup if there are dependencies

1 participant