elsa-workflows/elsa-core · error · ProviderHttpException
Timeout
Timeout
Error message
ProviderHttpException(ProviderHttpFailure.Timeout)
What it means
Each provider request runs under a linked CancellationTokenSource cancelled after ProviderEgress.RequestTimeout. If that internal timeout fires (the caller's own token is NOT cancelled — hence the when filter), SendAsync throws ProviderHttpException(ProviderHttpFailure.Timeout). It distinguishes the library's per-request timeout from caller-initiated cancellation, which propagates as OperationCanceledException instead.
Solutions
- Increase ProviderEgress.RequestTimeout in ExternalAuthenticationOptions if the provider legitimately needs longer (e.g. 30s instead of the default).
- Retry the request with backoff — timeouts are often transient; wrap the call in a Polly-style retry or your own loop.
- Diagnose the provider/network: curl -w with timing against the endpoint to see if DNS, connect, TLS, or server response is the slow stage; check provider status pages.
- Check ConnectTimeout as well — a stalled TCP connection is bounded by ConnectTimeout, so raising RequestTimeout alone may not help connection-level stalls.
Example fix
// before
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5)); // request timeout too tight
// after — configured via options
services.AddOptions<ExternalAuthenticationOptions>()
.Configure(o => o.ProviderEgress.RequestTimeout = TimeSpan.FromSeconds(30)); Defensive patterns
Strategy: retry
Validate before calling
// pre-flight reachability probe with explicit timing before using a provider endpoint
using var probe = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
var sw = Stopwatch.StartNew();
var ok = (await probe.GetAsync(providerUrl)).IsSuccessStatusCode;
if (!ok || sw.Elapsed > TimeSpan.FromSeconds(5))
logger.LogWarning("Provider endpoint slow or unreachable ({Elapsed}ms): {Url}", sw.ElapsedMilliseconds, providerUrl); Type guard
static bool IsEgressTimeout(ProviderHttpException ex) => ex.Failure == ProviderHttpFailure.Timeout;
Try / catch
for (var attempt = 1; attempt <= 3; attempt++)
{
try
{
return await client.GetAsync(uri, kind, ct);
}
catch (ProviderHttpException ex) when (ex.Failure == ProviderHttpFailure.Timeout && attempt < 3)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), ct); // exponential backoff
}
} Prevention
- Size ProviderEgress.RequestTimeout above the provider's p99 latency; measure it rather than guessing.
- Retry timeouts with exponential backoff — they are frequently transient, unlike DestinationRejected.
- Check ConnectTimeout separately: a stalled connection is bounded by ConnectTimeout even if RequestTimeout is generous.
- Alert on recurring Timeout failures per provider — persistent timeouts indicate a provider outage or network path problem, not a tuning issue.
When it happens
Trigger: Calling GetAsync/PostFormAsync against a provider that takes longer than ProviderEgress.RequestTimeout to respond — slow token endpoint, hanging TLS handshake/connection (also bounded by ConnectTimeout), or a discovery endpoint stalled mid-response. Thrown only when the elapsed timeout triggered the cancellation and the caller's token is still live.
Common situations: Provider under load or rate-limiting delaying responses beyond the default timeout; network firewall silently dropping packets so the connection hangs until timeout; oversized/slow discovery or JWKS responses on high-latency links; misjudging the timeout budget when chaining token + userinfo calls.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- The identity provider metadata could not be resolved.
- The identity provider token exchange failed.
- The identity provider signing keys could not be resolved.
- RedirectRejected
- TransportFailure
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/18d1eafdc57bf086.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ProviderHttpClientFactory.cs:118
throw new ProviderHttpException(ProviderHttpFailure.RedirectRejected);
current = new(current, response.Headers.Location);
continue;
}
if (!response.IsSuccessStatusCode)
return new(response.StatusCode, []);
return new(response.StatusCode, await ReadResponseBodyAsync(response, kind, timeout.Token));
}
}
catch (OutboundDestinationException)
{
throw new ProviderHttpException(ProviderHttpFailure.DestinationRejected);
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
throw new ProviderHttpException(ProviderHttpFailure.Timeout);
}
catch (ProviderHttpException)
{
throw;
}
catch (Exception) when (!cancellationToken.IsCancellationRequested)
{
throw new ProviderHttpException(ProviderHttpFailure.TransportFailure);
}
}
private async Task<byte[]> ReadResponseBodyAsync(HttpResponseMessage response, ProviderResponseKind kind, CancellationToken cancellationToken)
{
var limit = GetResponseLimit(kind);
var contentLength = response.Content.Headers.ContentLength;
if (contentLength is not null && contentLength > limit)
throw new ProviderHttpException(ProviderHttpFailure.ResponseTooLarge);
View on GitHub (pinned to fe9217bdfa)