dotnet/yarp · error · InvalidOperationException

Chosen destination has no model set: '{destination.Destinati

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.

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)

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.