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

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, 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.

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
  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. 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

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

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


# 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →