Music Assistant Provider System Architecture: How Loading, Unloading, and Dependencies Work
Music Assistant treats every integration as a provider managed through a lifecycle in music_assistant/mass.py, where load_provider() orchestrates manifest validation, dependency checks, and isolated module imports, while unload_provider() recursively cleans up dependents and resources.
The music-assistant/server repository implements a modular provider system that treats music services, player speakers, and metadata sources as pluggable components. Understanding this architecture is essential for developers extending the platform or troubleshooting integration failures.
Provider Discovery and Manifest Loading
Manifest Structure and Discovery
On server start, __load_provider_manifests() in music_assistant/mass.py traverses the music_assistant/providers/ directory, parsing each manifest.json to populate self._provider_manifests. These JSON files declare the provider's domain, type (music, player, metadata), pip requirements, and dependency constraints.
Built-in vs. Regular Providers
The system distinguishes between built-in providers—such as core controllers and the Home Assistant integration—and regular providers (third-party services). Built-in providers load unconditionally, even in safe mode, while regular providers initialize after core startup based on the DEFAULT_PROVIDERS list and user configuration.
The Provider Lifecycle
Loading a Provider
The entry point load_provider() cancels pending reload timers before delegating to load_provider_config(). The private _load_provider() method performs the heavy lifting:
- Unloads existing instances with the same ID via
await self.unload_provider(conf.instance_id) - Validates configuration against the manifest
- Checks singleton restrictions and multi-instance rules
- Imports the provider module via
load_provider_module(domain, requirements)frommusic_assistant/helpers/util.py, which installs pip dependencies in an isolated environment - Executes the provider's async
setup()andhandle_async_init()methods - Registers the instance in
self._providers, setsavailable=True, fires a ready event, and triggers discovery
Dependency Resolution
Before instantiation, _load_provider() inspects prov_manifest.depends_on. If a required provider is not loaded, the function returns early. The system automatically retries once the dependency becomes available, triggered by load_provider_config() after successful dependency loads.
Unloading and Cleanup
unload_provider() handles removal through several steps:
- Unschedules music synchronization jobs
- Notifies
playersandmusiccontrollers of the removal - Recursively unloads any providers that depend on the one being removed
- Invokes the provider's
unload(is_removed)method for custom cleanup - Purges internal dictionaries and discovery state
Error Handling and Retry Logic
When a provider raises MusicAssistantError (e.g., missing hardware), load_provider() catches the exception, writes the error to last_error in the config, and optionally schedules a retry using self.call_later(..., self.load_provider, ...). Fatal errors are logged without crashing the server.
Programmatic Provider Management
Developers can interact with the system directly.
Loading a provider:
await mass.load_provider("spotify_1", allow_retry=True)
Unloading a provider:
await mass.unload_provider("spotify_1")
Manifest example with dependencies:
{
"domain": "spotify",
"type": "music",
"builtin": false,
"multi_instance": false,
"depends_on": "auth",
"requirements": ["spotipy>=2.20"]
}
Dynamic module loading:
async def load_provider_module(domain: str, requirements: list[str]) -> ProviderModuleType:
# install missing pip requirements if needed, then import the provider's __init__.py
# returns a module that exposes a `setup(mass, manifest, config)` coroutine
...
Summary
- Providers are discovered from
music_assistant/providers/viamanifest.jsonfiles loaded by__load_provider_manifests() - The lifecycle is orchestrated in
music_assistant/mass.pythroughload_provider(),_load_provider(), andunload_provider() - Dependency resolution ensures providers load only after their dependencies are satisfied, with automatic retry logic
- Isolated module loading via
load_provider_module()inmusic_assistant/helpers/util.pyhandles pip requirements safely - Recursive cleanup during unloading prevents orphaned dependent providers
- Error handling uses
MusicAssistantErrorcatching with configurable retry timers viacall_later()
Frequently Asked Questions
How does Music Assistant handle circular provider dependencies?
The system checks prov_manifest.depends_on before instantiation. If a dependency is missing, _load_provider() returns early without error, and load_provider_config() triggers a retry once the dependency loads. This lazy evaluation prevents circular deadlock, though the manifest structure should ideally declare acyclic dependencies.
What happens when a provider fails to load due to missing hardware?
The load_provider() method catches MusicAssistantError exceptions, stores the error message in the provider config's last_error field, and optionally schedules a retry using self.call_later(). The server continues running, and the provider remains in a failed state until manually reloaded or the issue resolves.
Can I run multiple instances of the same provider simultaneously?
This depends on the multi_instance flag in the provider's manifest.json. If set to false (as seen in the Spotify manifest), the system enforces singleton restrictions during _load_provider(). If true, you can create multiple configurations with unique instance IDs.
How does the system isolate provider code from the core server?
The load_provider_module() function in music_assistant/helpers/util.py imports provider code in an isolated environment and automatically installs required pip packages specified in the manifest's requirements array. This prevents dependency conflicts between providers while ensuring the core server remains stable.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →