LuckyPennySoftware/MediatR · error · InvalidOperationException

{openBehaviorType.Name} must implement {typeof(IStreamPipeli

Error message

{openBehaviorType.Name} must implement {typeof(IStreamPipelineBehavior<,>).FullName}

What it means

Thrown by AddOpenStreamBehavior when the supplied open generic type does not implement IStreamPipelineBehavior<TRequest,TResponse>. MediatR scans the type's implemented generic interfaces (via GetInterfaces().GetGenericTypeDefinition()) and requires at least one to equal the open IStreamPipelineBehavior<,> definition so it can register it as a streaming pipeline behavior ServiceDescriptor.

Source

Thrown at src/MediatR/MicrosoftExtensionsDI/MediatrServiceConfiguration.cs:336

    /// <summary>
    /// Registers an open stream behavior type against the <see cref="IStreamPipelineBehavior{TRequest,TResponse}"/> open generic interface type
    /// </summary>
    /// <param name="openBehaviorType">An open generic stream behavior type</param>
    /// <param name="serviceLifetime">Optional service lifetime, defaults to <see cref="ServiceLifetime.Transient"/>.</param>
    /// <returns>This</returns>
    public MediatRServiceConfiguration AddOpenStreamBehavior(Type openBehaviorType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
    {
        if (!openBehaviorType.IsGenericType)
        {
            throw new InvalidOperationException($"{openBehaviorType.Name} must be generic");
        }

        var implementedGenericInterfaces = openBehaviorType.GetInterfaces().Where(i => i.IsGenericType).Select(i => i.GetGenericTypeDefinition());
        var implementedOpenBehaviorInterfaces = new HashSet<Type>(implementedGenericInterfaces.Where(i => i == typeof(IStreamPipelineBehavior<,>)));

        if (implementedOpenBehaviorInterfaces.Count == 0)
        {
            throw new InvalidOperationException($"{openBehaviorType.Name} must implement {typeof(IStreamPipelineBehavior<,>).FullName}");
        }

        foreach (var openBehaviorInterface in implementedOpenBehaviorInterfaces)
        {
            StreamBehaviorsToRegister.Add(new ServiceDescriptor(openBehaviorInterface, openBehaviorType, serviceLifetime));
        }

        return this;
    }

    /// <summary>
    /// Register a closed request pre processor type
    /// </summary>
    /// <typeparam name="TServiceType">Closed request pre processor interface type</typeparam>
    /// <typeparam name="TImplementationType">Closed request pre processor implementation type</typeparam>
    /// <param name="serviceLifetime">Optional service lifetime, defaults to <see cref="ServiceLifetime.Transient"/>.</param>
    /// <returns>This</returns>
    public MediatRServiceConfiguration AddRequestPreProcessor<TServiceType, TImplementationType>(ServiceLifetime serviceLifetime = ServiceLifetime.Transient)

View on GitHub (pinned to 916ef1b3d6)

Solutions

  1. Make the open generic type implement IStreamPipelineBehavior<TRequest,TResponse> where TRequest and TResponse are the open type parameters (e.g. class MyBehavior<TRequest,TResponse> : IStreamPipelineBehavior<TRequest,TResponse>).
  2. If the type is actually a non-streaming behavior, register it with AddOpenBehavior (IPipelineBehavior<,>) instead of AddOpenStreamBehavior.
  3. Verify the interface spelling and arity: stream behavior is the two-arity IStreamPipelineBehavior<TRequest,TResponse> and its Handle returns IAsyncEnumerable<TResponse>.

Example fix

// before
cfg.AddOpenStreamBehavior(typeof(MyPipelineBehavior<,>)); // implements IPipelineBehavior<,>

// after
public class MyStreamBehavior<TRequest, TResponse>
    : IStreamPipelineBehavior<TRequest, TResponse>
    where TRequest : IStreamRequest<TResponse>
{
    public IAsyncEnumerable<TResponse> Handle(
        TRequest request, StreamHandlerDelegate<TResponse> next,
        CancellationToken ct) => next(request, ct);
}

cfg.AddOpenStreamBehavior(typeof(MyStreamBehavior<,>));
Defensive patterns

Strategy: validation

Validate before calling

// Validate before registering the open stream behavior
static bool ImplementsOpenStreamBehavior(Type t) =>
    t.IsGenericType &&
    t.GetInterfaces()
        .Where(i => i.IsGenericType)
        .Select(i => i.GetGenericTypeDefinition())
        .Any(i => i == typeof(IStreamPipelineBehavior<,>));

if (ImplementsOpenStreamBehavior(typeof(MyBehavior<,>)))
    cfg.AddOpenStreamBehavior(typeof(MyBehavior<,>));

Type guard

static bool IsOpenStreamBehavior(Type t) =>
    t.IsGenericType &&
    t.GetInterfaces()
        .Where(i => i.IsGenericType)
        .Select(i => i.GetGenericTypeDefinition())
        .Contains(typeof(IStreamPipelineBehavior<,>));

Prevention

When it happens

Trigger: Calling services.AddMediatR(cfg => cfg.AddOpenStreamBehavior(typeof(SomeOpenGeneric))) where SomeOpenGeneric is generic but implements IPipelineBehavior<,> (the non-stream behavior) instead of IStreamPipelineBehavior<,>, or implements no pipeline interface at all.

Common situations: Confusing the stream vs. non-stream pipeline behaviors; passing a regular request behavior type into the stream registration API; copying a type name from a non-streaming sample; renaming/refactoring a behavior so it no longer declares the stream interface.

Related errors


AI-assisted analysis of LuckyPennySoftware/MediatR@916ef1b3d6 (2026-08-13). Data as JSON: /api/errors/f987db10969c73f8. Report an issue: GitHub.