pymediate
Guide

Troubleshooting

Identify installation, handler registration, service wiring, and type-checking problems.

PyMediate errors distinguish handler class registration from handler instance resolution. Check which stage failed before changing the service configuration.

Dispatch errors

ErrorMeaningFirst check
HandlerNotFoundErrorNo handler class is registered for the exact request typeWas the module that defines the handler imported?
ServiceNotFoundErrorA handler class is registered, but the provider cannot supply its instanceWas the instance added before Services?
HandlerAlreadyRegisteredErrorA second request or stream handler class was defined for one request typeSearch for both class definitions or duplicate imports
ExceptionGroup from publish()One or more notification handlers raisedInspect the grouped exceptions with except*

HandlerNotFoundError

Request and stream handler classes register when Python defines them. The mediator raises HandlerNotFoundError when its registry has no handler class for the concrete request type.

Check that:

  1. the module containing the handler has been imported before dispatch;
  2. the handler's type argument names the request you are sending;
  3. you are using send() for Request and stream() for StreamRequest; and
  4. you are not sending a derived request while only its base request has a handler.

Dispatch uses type(request), not isinstance(). A handler for a base request does not receive a derived request.

Adding an instance to Services does not create the request-to-handler class mapping. That mapping comes from defining RequestHandler[RequestType] or StreamRequestHandler[RequestType]. Conversely, once a mapping exists, a missing instance raises ServiceNotFoundError, not HandlerNotFoundError.

ServiceNotFoundError

Pass every handler instance to Services(...) and hand that collection to Mediator. Resolution is by exact type, so a handler registered as a subclass does not satisfy a request for its base class. With DependencyInjectorServiceProvider, check that the container declares a provider whose resolved type is the exact handler class.

Publishing resolves every subscriber instance before any handler runs. One missing notification handler instance therefore raises ServiceNotFoundError and prevents partial delivery.

HandlerAlreadyRegisteredError

Each Request or StreamRequest type has one handler class in a process. Defining a second class for the same request type raises this error immediately, before either handler is instantiated.

Common causes are:

  • two implementations for one request type;
  • a test suite that defines replacement handler classes in several files;
  • importing the same source module under two module names; or
  • defining asynchronous and synchronous handlers for the same request type.

Keep one handler class and vary its dependencies through the constructor. Use distinct request types when the operations have different contracts. Notification handlers are different: several NotificationHandler classes may subscribe to one notification type.

Construction errors

ServiceAlreadyRegisteredError

Services(...) received two instances of the same concrete type. Only one instance per exact type is resolvable, so the second could never be reached.

Check that:

  1. no handler or behavior instance is passed twice — a Services(*handlers, *behaviors) call can repeat one if a class appears in both lists; and
  2. two distinct services really do have distinct types. Two instances of one class configured differently cannot both be resolved; give them separate classes if both must be.

To replace a service on purpose — swapping a fake into a production wiring for a test — combine two collections with | instead. The right operand wins, and a shared type is not an error there.

InvalidPipelineBehaviorsError

Mediator(services, behaviors=[...]) validates the behaviors sequence once, at construction. Each entry must be a PipelineBehavior subclass of the mediator's variant (asynchronous for pymediate.Mediator, synchronous for pymediate.sync.Mediator), registered with services, and listed at most once.

Check that:

  1. every class in behaviors was added to Services (or the container behind your custom provider) before building the provider;
  2. behaviors mixes only classes from the mediator's own variant - an asynchronous behavior in a synchronous mediator's behaviors list, or vice versa, fails the subclass check; and
  3. no class appears twice in the list.

A behavior registered with the provider but left out of behaviors does not raise - it is simply not part of the pipeline. If a behavior you expect to run does not, check behaviors first before suspecting should_apply().

Definition-time validation errors

PyMediate validates handler declarations when Python executes their class bodies. An error at import time usually means the declaration and __call__ signature disagree.

InvalidHandlerSignatureError

For a request handler, check all of the following:

  • __call__ has only self and one request parameter;
  • the request parameter and return value both have annotations;
  • the parameter annotation is the exact type named in RequestHandler[...];
  • the asynchronous class uses async def, while the synchronous class uses def; and
  • a notification handler returns None.

Stream handlers must be generators. Their return annotation must be AsyncIterator[ChunkT] for the asynchronous API or Iterator[ChunkT] for the synchronous API. Returning an iterator from a function that contains no yield is rejected.

ResponseTypeMismatchError

The return annotation on a request handler must equal the response type declared by its request. For example, a handler for PlaceOrder, where PlaceOrder(Request[OrderReceipt]), must annotate its return as OrderReceipt.

This check compares annotations when Python defines the handler class. PyMediate does not inspect the value returned on every call. Keep a static type checker enabled so an implementation that returns a value inconsistent with its annotation is also reported.

Invalid request, notification, or stream types

  • InvalidRequestTypeError means a request handler's type argument does not declare a response through Request[ResponseT].
  • InvalidNotificationTypeError means a notification handler's type argument does not inherit Notification.
  • InvalidStreamRequestTypeError means a stream handler's type argument does not declare a chunk type through StreamRequest[ChunkT].

These constraints are also represented in the type annotations, so mypy and pyright can report many of them before import-time validation runs.

Type inference problems

If mediator.send() is inferred as returning Any, check that the concrete request parameterizes Request:

from dataclasses import dataclass

from pymediate import Request


@dataclass(frozen=True)
class OrderReceipt:
    order_id: int
    summary: str


@dataclass(frozen=True)
class PlaceOrder(Request[OrderReceipt]):
    customer_id: int
    item: str
    quantity: int

A bare Request loses the relationship between the request and response types. Similarly, a bare StreamRequest loses the stream's chunk type. See type safety for the boundary between static checking and definition-time validation.

Installation problems

PyMediate requires Python 3.12 or newer. Check that the interpreter used to run the application is the same environment in which the package was installed.

DependencyInjectorServiceProvider requires the optional di extra:

pip install 'pymediate[di]'

Getting help

  1. Check the guide and API reference.
  2. Search existing issues.
  3. Ask in GitHub Discussions.
  4. Report a reproducible bug.

On this page