AdapterNotFoundError¶
Overview¶
AdapterNotFoundError is raised when you try to resolve a port (Protocol or ABC) but
no adapter is registered for the current profile.
This error means:
No adapter exists for this port + profile combination, OR
An adapter exists but for a different profile, OR
The adapter wasn’t imported before container scanning
Example Error¶
Adapter Not Found: No adapter for EmailPort in profile 'test'
Registered: SendGridAdapter (production), ConsoleEmailAdapter (development)
Context:
- port: EmailPort
- profile: test
- available_adapters: [('SendGridAdapter', ['production']), ('ConsoleEmailAdapter', ['development'])]
-> See: https://dioxide.readthedocs.io/en/stable/troubleshooting/adapter-not-found.html
Common Causes¶
Profile Mismatch (Most Common)¶
You have an adapter registered for one profile, but you’re scanning with a different profile.
from dioxide import Container, Profile, adapter
class EmailPort(Protocol):
async def send(self, to: str, subject: str, body: str) -> None: ...
# Only PRODUCTION adapter exists
@adapter.for_(EmailPort, profile=Profile.PRODUCTION)
class SendGridAdapter:
async def send(self, to: str, subject: str, body: str) -> None:
...
# But you're scanning with TEST profile
container = Container(profile=Profile.TEST) # AdapterNotFoundError!
Solution: Add an adapter for the TEST profile:
@adapter.for_(EmailPort, profile=Profile.TEST)
class FakeEmailAdapter:
def __init__(self):
self.sent_emails = []
async def send(self, to: str, subject: str, body: str) -> None:
self.sent_emails.append({"to": to, "subject": subject, "body": body})
Missing Adapter Completely¶
No adapter is registered for the port at all.
class DatabasePort(Protocol):
async def query(self, sql: str) -> list[dict]: ...
@service
class UserService:
def __init__(self, db: DatabasePort): # Depends on DatabasePort
self.db = db
container = Container(profile=Profile.PRODUCTION) # AdapterNotFoundError!
Solution: Register an adapter for the port:
@adapter.for_(DatabasePort, profile=Profile.PRODUCTION)
class PostgresAdapter:
async def query(self, sql: str) -> list[dict]:
...
Adapter Not Imported¶
The adapter module wasn’t imported before scanning, so the decorator never executed.
# myapp/adapters/email.py
@adapter.for_(EmailPort, profile=Profile.PRODUCTION)
class SendGridAdapter:
...
# myapp/main.py
# Forgot to import myapp.adapters.email!
container = Container(profile=Profile.PRODUCTION) # AdapterNotFoundError!
Solution: Either import the module or use package scanning:
# Option 1: Explicit import
import myapp.adapters.email # Decorator runs at import
container = Container(profile=Profile.PRODUCTION)
# Option 2: Package scanning (recommended)
container = Container(profile=Profile.PRODUCTION)
container.scan(package="myapp.adapters")
Solutions¶
Universal Adapter (Profile.ALL)¶
If an adapter should work in all profiles (e.g., logging), use Profile.ALL:
@adapter.for_(LoggerPort, profile=Profile.ALL)
class ConsoleLogger:
def log(self, message: str) -> None:
print(message)
# Works with any profile
container = Container(profile=Profile.TEST)
logger = container.resolve(LoggerPort) # Works!
Multiple Profile Registration¶
Register an adapter for multiple profiles:
@adapter.for_(EmailPort, profile=[Profile.TEST, Profile.DEVELOPMENT])
class FakeEmailAdapter:
...
Debugging Tips¶
Check the error message: It lists available adapters and their profiles
Verify profile spelling: Profiles are case-insensitive but must match
Use Profile enum: Prefer
Profile.TESTover'test'to catch typosList registrations: Use
container.scan(package="...")for auto-discovery
Best Practices¶
Every production adapter needs a test fake: Create a fast, in-memory fake for testing
Use Profile.ALL sparingly: Only for truly universal adapters like logging
Fail fast at startup: Resolve all services at startup to catch missing adapters early
Use explicit profiles: Prefer
Profileenum over strings
See Also¶
Hexagonal Architecture with dioxide - How ports and adapters work
Scoping Guide - Understanding profiles and scopes
ServiceNotFoundError - Similar error for services