dotnet/yarp · error · InvalidOperationException
The request cannot be forwarded, the response has already st
Error message
The request cannot be forwarded, the response has already started
What it means
Thrown by HttpForwarder.SendAsync when the response has already started (headers sent / body written) on the HttpContext. Once the response has started, YARP cannot replace it with the destination's response, so forwarding is impossible. The check uses RequestUtilities.IsResponseSet.
Source
Thrown at src/ReverseProxy/Forwarder/HttpForwarder.cs:112
=> SendAsync(context, destinationPrefix, httpClient, requestConfig, transformer, CancellationToken.None);
public async ValueTask<ForwarderError> SendAsync(
HttpContext context,
string destinationPrefix,
HttpMessageInvoker httpClient,
ForwarderRequestConfig requestConfig,
HttpTransformer transformer,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(destinationPrefix);
ArgumentNullException.ThrowIfNull(httpClient);
ArgumentNullException.ThrowIfNull(requestConfig);
ArgumentNullException.ThrowIfNull(transformer);
if (RequestUtilities.IsResponseSet(context.Response))
{
throw new InvalidOperationException("The request cannot be forwarded, the response has already started");
}
// HttpClient overload for SendAsync changes response behavior to fully buffered which impacts performance
// See discussion in https://github.com/dotnet/yarp/issues/458
if (httpClient is HttpClient)
{
throw new ArgumentException($"The http client must be of type HttpMessageInvoker, not HttpClient", nameof(httpClient));
}
// "http://a".Length = 8
if (destinationPrefix is null || destinationPrefix.Length < 8)
{
throw new ArgumentException("Invalid destination prefix.", nameof(destinationPrefix));
}
ForwarderTelemetry.Log.ForwarderStart(destinationPrefix);
var activityCancellationSource = ActivityCancellationTokenSource.Rent(requestConfig?.ActivityTimeout ?? DefaultTimeout, context.RequestAborted, cancellationToken);View on GitHub (pinned to bd11867bee)
Solutions
- Ensure no middleware writes to the response before HttpForwarder.SendAsync runs.
- Short-circuit (return) after any response write instead of continuing to the forwarder.
- Check context.Response.HasStarted before invoking the forwarder and handle gracefully.
Example fix
// before context.Response.StatusCode = 401; await forwarder.SendAsync(context, address, client, reqConfig, transformer, ct); // throws // after context.Response.StatusCode = 401; return; // do not forward
Defensive patterns
Strategy: validation
Validate before calling
if (RequestUtilities.IsResponseSet(context.Response))
{
// response already started; cannot forward
return;
} Type guard
static bool CanStillForward(HttpContext ctx) => !RequestUtilities.IsResponseSet(ctx.Response);
Try / catch
try { await forwarder.SendAsync(context, address, client, reqConfig, transformer, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("response has already started"))
{ logger.LogWarning("Cannot forward; response already started."); } Prevention
- Do not write to context.Response before forwarding.
- Short-circuit (return) after any response write.
- Check HasStarted before invoking the forwarder.
When it happens
Trigger: A middleware or endpoint writes to context.Response (status, headers, body) before the forwarder runs. Calling SendAsync after an error handler or middleware that already produced output.
Common situations: Exception-handling middleware writes a response then calls the forwarder. Authentication/authorization middleware sets a 401 then continues. Custom logging middleware writes diagnostics to the response.
Related errors
- The IReverseProxyFeature Destinations collection was not set
- The IReverseProxyFeature Destinations collection was not set
- Current request can't be delegated. Either the request body
- The IReverseProxyFeature Cluster was not set.
- Chosen destination has no model set: '{destination.Destinati
AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13).
Data as JSON: /api/errors/8d1ff03f056e1ea0.
Report an issue: GitHub.