mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-07-02 11:25:37 +01:00
0f881d69fe
* apps: model client-state Apps* errors as APIError Follow-up on #6739: with HassioError now logged and captured by Sentry in api_process, a handful of Apps* exceptions raised from AppManager.install/update/rebuild and AppModel._validate_availability surfaced as "unexpected" 400s with a noisy log entry and a Sentry event, even though they are all user/client-state errors (clicked install on an already-installed add-on, "no update available", local and store versions diverged, system architecture/machine/HA version incompatible, etc.). The dominant offender is SUPERVISOR-1JVV ("No update available for app core_mosquitto", ~19k events / ~12k users), but several siblings show the same shape. Map these through properly so the API returns clean, structured 400s: - Add modeled APIError subclasses in exceptions.py for the previously raw raises in apps/manager.py: AppAlreadyInstalledError, AppNotFoundError, AppNotInstalledError, AppNotInStoreError, AppNoUpdateAvailableError, AppRebuildVersionChangedError, AppRebuildImageBasedError. Each gets a stable error_key, a message_template, and an "addon" extra_field. - Add APIError to AppNotSupportedArchitectureError, AppNotSupportedMachineTypeError and AppNotSupportedHomeAssistantVersionError so they behave the same as the other Apps* APIErrors instead of being treated as unexpected. - Pass the app's display name (app.name from the add-on config) instead of the slug to extra_fields wherever an App or AppModel is available at the raise site, so users see "Mosquitto broker" rather than "core_mosquitto" in error messages. Slug is only used as a fallback when no app object exists (install of an unknown slug, update/rebuild of a slug that is not installed). - Update raise sites in apps/manager.py and apps/model.py to use the new typed exceptions and the addon= keyword. These are all runtime states users hit during normal interaction with the apps UI, not Supervisor bugs worth paging on. * apps: include slug alongside name in Apps* APIError extra_fields Address review feedback from #6856: clients still need the slug to look up additional add-on information (the name is for display only), and we should be consistent about it across the Apps* errors touched by this PR. Every Apps* APIError raised with an App/AppModel available now carries both `addon` (display name, used by the message_template) and `slug` in extra_fields. Raise sites in apps/manager.py and apps/model.py pass both. The two errors raised before an app object exists keep slug-only extra_fields and use {slug} in their message: - AppNotFoundError (install of an unknown slug) - AppNotInstalledError (update/rebuild of a slug not in self.local) Pre-existing Apps* APIErrors outside the scope of this PR (AppUnknownError, AppConfigurationInvalidError, AppBootConfigCannot ChangeError, AppNotRunningError, AppPortConflict, AppNotSupportedWrite StdinError, AppBuild*) will be migrated in a follow-up. * apps: introduce AppAPIError base for uniform addon/slug extra_fields Address review follow-up on #6856: the addon/slug convention was enforced only by hand-rolled __init__s, easy to drift on (forget slug, use a different key, etc.). Promote it to a base class that owns the shape of extra_fields for all App-related API errors. - Add AppAPIError(AppsError, APIError). Its __init__ takes `app: AppModel | App | AppStore | str` and uniformly populates extra_fields with `addon` (display name) and `slug`. Pass a string when no app object exists; only `slug` is set in that case. Extra per-error fields flow through **extra_fields and merge with the defaults. - Convert the new exceptions added in this PR (AppAlreadyInstalledError, AppNotFoundError, AppNotInstalledError, AppNotInStoreError, AppNoUpdateAvailableError, AppRebuildVersionChangedError) into thin subclasses that only declare error_key and message_template -- the __init__ is inherited. - Migrate the AppNotSupported* errors (architecture, machine type, HA version) and AppRebuildImageBasedError to use AppAPIError too; their bespoke per-error fields go through **extra_fields. They keep inheriting AppNotSupportedError so `except AppNotSupportedError` callers (e.g., AppModel._available) still work; MRO routes __init__ through AppAPIError. - Update raise sites in apps/manager.py and apps/model.py to pass `app=<obj-or-slug>` instead of repeating `addon=...` and `slug=...`. Pre-existing App* APIErrors outside this PR's scope (AppUnknownError, AppConfigurationInvalidError, AppBootConfigCannotChangeError, AppNotRunningError, AppPortConflict, AppNotSupportedWriteStdinError, AppBuild*) will be migrated to AppAPIError in a follow-up; the base class is in place for them. * apps: tighten AppAPIError model per review Address mdegat01's two follow-ups on #6856 (review approved as-is, this is the cleanup): - AppNotFoundError and AppNotInstalledError are raised before any App/AppModel object exists (unknown slug; not-installed slug). Pull them out of AppAPIError and inherit (AppsError, APIError) directly with a slug-only __init__ + {slug} message_template. Removes the conceptually-wrong str branch from AppAPIError.__init__: it now strictly requires an app-like object with .name and .slug. - Restore per-class typed __init__ on AppNotSupportedArchitectureError, AppNotSupportedMachineTypeError and AppNotSupportedHomeAssistantVersionError so callers get an explicit signature for the bespoke architectures/machine_types/version params instead of dumping them through **extra_fields. Each override just delegates to AppAPIError.__init__, which keeps ownership of the addon/slug shape. The list-joining for architectures/machine_types moves back into the override (raise sites pass the raw list again). AppRebuildImageBasedError takes no bespoke fields and stays a plain AppAPIError subclass.