dotnet/yarp · error · InvalidOperationException

Chosen destination has no model set

Error message

Chosen destination has no model set: '{destination.DestinationId}'

What it means

Thrown by ForwarderMiddleware.Invoke when the selected destination's Model property is null. DestinationState.Model holds the runtime DestinationConfig (address, health); a null model means the destination is in an incomplete/transitional state, e.g., it was removed during a config reload between selection and use. The forwarder cannot proceed without an address to proxy to.

Solutions

  1. Reduce config reload churn or batch updates to minimize the race window.
  2. If custom-constructing DestinationState, always set Model to a valid DestinationModel.
  3. Treat as transient: the next request after reload should succeed; add retry at the caller if needed.

Example fix

// before — custom DestinationState without Model
var dest = new DestinationState("d1"); // Model stays null
// after
var dest = new DestinationState("d1",
    new DestinationModel(new DestinationConfig { Address = "https://backend/api" }));
Defensive patterns

Strategy: try-catch

Validate before calling

if (destination.Model is null)
{
    context.Response.StatusCode = 503;
    return;
}

Type guard

static bool HasModel(DestinationState d) => d.Model is not null;

Try / catch

try { await _forwarder.SendAsync(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("no model set"))
{ context.Response.StatusCode = 503; // transient; reload race }

Prevention

When it happens

Trigger: A destination is selected from AvailableDestinations but its Model is null — typically because a config reload concurrently removed/ replaced the destination, leaving a stale DestinationState in the collection. Can also indicate a bug in destination lifecycle management.

Common situations: High-frequency config reloads racing with in-flight requests. Custom code injecting DestinationState objects without a Model. Destinations removed dynamically while requests are being routed.

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/b042bf6fa5e3ad3d. Report an issue: GitHub.

Appendix: source

Thrown at src/ReverseProxy/Forwarder/ForwarderMiddleware.cs:76

            activity?.AddError("Proxy forwarding failed", "No available destinations to forward to");
            return;
        }

        var destination = destinations[0];
        if (destinations.Count > 1)
        {
            var random = _randomFactory.CreateRandomInstance();
            Log.MultipleDestinationsAvailable(_logger, cluster.ClusterId);
            destination = destinations[random.Next(destinations.Count)];
        }

        reverseProxyFeature.ProxiedDestination = destination;
        activity?.AddTag("proxy.destination_id", destination.DestinationId);

        var destinationModel = destination.Model;
        if (destinationModel is null)
        {
            throw new InvalidOperationException($"Chosen destination has no model set: '{destination.DestinationId}'");
        }

        try
        {
            cluster.ConcurrencyCounter.Increment();
            destination.ConcurrencyCounter.Increment();
            ForwarderTelemetry.Log.ForwarderInvoke(cluster.ClusterId, route.Config.RouteId, destination.DestinationId);

            var clusterConfig = reverseProxyFeature.Cluster;
            var result = await _forwarder.SendAsync(context, destinationModel.Config.Address, clusterConfig.HttpClient,
                clusterConfig.Config.HttpRequest ?? ForwarderRequestConfig.Empty, route.Transformer);

            activity?.SetStatus((result == ForwarderError.None) ? ActivityStatusCode.Ok : ActivityStatusCode.Error);
        }
        finally
        {
            destination.ConcurrencyCounter.Decrement();
            cluster.ConcurrencyCounter.Decrement();

View on GitHub (pinned to bd11867bee)