# Music Assistant Provider Error Handling and Retry Mechanism: How load_provider Works

> Learn how Music Assistant handles provider errors and implements a robust retry mechanism with load_provider. Discover automatic retries and permanent disabling for unsupported hardware.

- Repository: [Music Assistant/server](https://github.com/music-assistant/server)
- Tags: internals
- Published: 2026-06-21

---

**Music Assistant automatically retries provider loads after 120 seconds when recoverable `MusicAssistantError` exceptions occur, while permanently disabling providers that encounter unsupported hardware errors without retry.**

Music Assistant orchestrates music, player, and metadata providers through a robust loading pipeline that handles transient failures gracefully. The `load_provider` method in the `music-assistant/server` repository implements sophisticated error classification, distinguishing between temporary outages and permanent hardware incompatibilities. This system ensures high availability by automatically rescheduling failed loads while preventing infinite retry loops for unrecoverable conditions.

## Provider Loading Pipeline Architecture

The provider loading system operates through three distinct entry points in [`music_assistant/mass.py`](https://github.com/music-assistant/server/blob/main/music_assistant/mass.py), each serving a specific purpose in the initialization chain.

### load_provider() – Public Entry Point with Retry Logic

The **`load_provider`** method serves as the primary public interface for provider initialization. It accepts an `instance_id`, determines whether a reload is necessary, and implements the **error handling retry logic** that defines the system's resilience.

When exceptions occur during loading, the method classifies them into distinct categories:

- **`UnsupportedSystemError`** – Indicates the provider cannot run on the current hardware. The method optionally removes the configuration entry and **never schedules a retry**.
- **`MusicAssistantError` subclasses** – Represent recoverable conditions like temporary network outages. The method schedules an automatic retry after **120 seconds** using `self.call_later(...)` with `allow_retry=True`.
- **Generic exceptions** – Treated as unrecoverable bugs; no retry is scheduled unless explicitly requested.

The retry timer uses a unique task identifier: `task_id = f"load_provider_{instance_id}"`. This timer is stored in `self._tracked_timers` and automatically cancelled if a new load request arrives for the same provider, preventing duplicate retry attempts. Source: lines 745-771 in [`music_assistant/mass.py`](https://github.com/music-assistant/server/blob/main/music_assistant/mass.py).

### load_provider_config() – Dependency Coordinator

The **`load_provider_config`** method wraps the private `_load_provider` call and ensures dependency chains remain synchronized. After a successful load, it iterates through all provider configurations and reloads any that depend on the newly loaded provider (`manifest.depends_on == prov_conf.domain`). This guarantees that provider chains initialize in the correct order and remain consistent during runtime updates. Source: lines 733-744.

### _load_provider() – Core Implementation

The private **`_load_provider`** method performs the actual heavy lifting of provider instantiation:

1. Validates the provider configuration against the manifest schema
2. Checks for duplicate single-instance providers
3. Resolves dependencies via the manifest
4. Loads the provider module using `load_provider_module` from [`music_assistant/helpers/util.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/util.py)
5. Invokes the provider's async `setup()` coroutine

If any stage raises an exception, the method converts it into a **`ProviderError`** via `_provider_error_from_exc` and stores it in the configuration. This serialized error surface to the UI, allowing users to see specific failure messages without parsing logs. Source: lines 1000-1071.

## Error Classification and Retry Behavior

The retry logic depends on precise exception categorization:

| Exception Type | Retry Behavior | Configuration Impact |
|----------------|----------------|---------------------|
| **UnsupportedSystemError** | Never retried | Optional removal via `remove_if_unsupported` |
| **MusicAssistantError** (subclass) | Retried after 120 seconds | Error stored in `last_error` for UI display |
| **Generic Exception** | No retry (unless `allow_retry=True`) | Treated as unrecoverable bug |

## Retry Implementation Mechanics

The retry system prevents resource exhaustion through careful timer management. When `load_provider` schedules a retry, it creates a delayed task that calls itself recursively with the same parameters. The `task_id` format ensures that only one retry timer exists per provider instance at any time.

If a user or system process manually triggers a reload before the retry timer fires, the existing timer is automatically cancelled. This prevents race conditions where multiple loading attempts might execute simultaneously. The implementation relies on `self.call_later(120, ...)` to defer execution without blocking the event loop.

## Dependency Chain Synchronization

When a provider successfully loads, `load_provider_config` walks the entire provider configuration tree to identify dependents. Any provider configuration whose `manifest.depends_on` matches the newly loaded provider's domain is automatically reloaded. This cascading update ensures that changes in core providers (like authentication handlers) propagate to dependent services (like specific music source integrations) without manual intervention.

## Manual Reload and Debugging Tools

For development and troubleshooting, Music Assistant exposes the **`debug_reload_provider`** tool in [`music_assistant/providers/fastmcp_server/tools/debug.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/fastmcp_server/tools/debug.py). This FastMCP tool provides direct access to the `_load_provider` primitive with additional safety controls:

- **Serialization**: Uses `reload_lock` to prevent concurrent reloads of the same provider
- **Confirmation polling**: Waits up to 5 seconds to verify the provider reaches an available state
- **Structured results**: Returns a `ReloadResult` containing `instance_id`, `duration_ms`, `new_available` boolean, and optional `last_error` string

The tool accepts an `instance_id` parameter and respects the `TIMEOUT_INTERACTIVE` default of 120 seconds for MCP operations.

## Practical Code Examples

### Programmatic Retry with Error Handling

```python
async def reload_with_retry(mass: MusicAssistant, instance_id: str) -> None:
    """Request provider reload with automatic retry on recoverable errors."""
    # allow_retry=True schedules a retry after 2 minutes if MusicAssistantError occurs

    await mass.load_provider(instance_id, allow_retry=True)

```

### Inspecting Failed Load Errors

```python
def get_last_error(mass: MusicAssistant, instance_id: str) -> str | None:
    """Retrieve the last error message from provider configuration."""
    prov_conf = mass.config.get_provider_config_sync(instance_id)
    return prov_conf.last_error.message if prov_conf.last_error else None

```

### Manual Reload via MCP Debug Tool

```python

# Pseudo-code for MCP client interaction

result = await mcp_client.call_tool(
    "debug_reload_provider",
    {"instance_id": "spotify_1"},
    timeout=120  # Respect TIMEOUT_INTERACTIVE

)

# Returns ReloadResult with duration_ms, new_available, last_error

```

## Summary

- **`load_provider`** schedules 120-second retries exclusively for `MusicAssistantError` subclasses, skipping retries for hardware incompatibility errors.
- **Retry timers** use unique `task_id` keys tied to `instance_id` and auto-cancel when new load requests arrive, preventing duplicate attempts.
- **`_load_provider`** converts all exceptions to `ProviderError` objects stored in configuration for UI consumption.
- **Dependency chains** automatically reload via `load_provider_config` when a parent provider initializes successfully.
- **`debug_reload_provider`** provides forced reload capability with 5-second availability polling and structured error reporting.

## Frequently Asked Questions

### How long does Music Assistant wait before retrying a failed provider load?

Music Assistant waits **120 seconds** (2 minutes) before retrying a failed load. This delay is implemented via `self.call_later(120, ...)` in the `load_provider` method and applies only to recoverable errors that subclass `MusicAssistantError`.

### What happens when a provider encounters an UnsupportedSystemError?

When a provider raises **`UnsupportedSystemError`**, Music Assistant recognizes that the current hardware cannot support the provider (e.g., missing required libraries or incompatible architecture). The system **never schedules a retry** and optionally removes the configuration entry entirely via the `remove_if_unsupported` flag, effectively disabling the provider permanently for that installation.

### Can I manually trigger a provider reload without waiting for the automatic retry?

Yes. You can programmatically call `await mass.load_provider(instance_id, allow_retry=True)` from any async context to force an immediate reload. Alternatively, use the **`debug_reload_provider`** FastMCP tool, which bypasses the retry timer entirely and executes `_load_provider` directly while holding a `reload_lock` to prevent race conditions.

### How does Music Assistant prevent duplicate retry timers for the same provider?

The system prevents duplicate timers by storing each retry task in `self._tracked_timers` using a unique identifier formatted as `f"load_provider_{instance_id}"`. When a new load request arrives for a provider that already has a pending retry timer, the existing timer is automatically cancelled before the new load attempt begins, ensuring only one retry schedule exists per provider instance.