elsa-workflows/elsa-core · error · ProviderHttpException
TransportFailure
TransportFailure
Error message
ProviderHttpException(ProviderHttpFailure.TransportFailure)
What it means
ProviderHttpClientFactory wraps all outbound HTTP calls to the external identity provider (discovery, token, userinfo) and normalizes every failure into a ProviderHttpException with a ProviderHttpFailure code. TransportFailure is the catch-all branch: any exception that is not a cancellation, timeout, or already-classified provider error is rethrown as TransportFailure. It signals a low-level HTTP/network problem (DNS, TLS, connection reset, malformed response stream) rather than an application-level rejection.
Solutions
- Verify the provider authority/base URL is correct and reachable from the machine running Elsa (curl the discovery endpoint, e.g. https://<authority>/.well-known/openid-configuration).
- Check network egress: firewall rules, proxy env vars (HTTP_PROXY/HTTPS_PROXY), and DNS resolution inside containers; add the CA certificate to the trust store if TLS interception is in place.
- Retry the operation — transport failures are often transient; configure HttpClient retry policies (Polly) at the HttpClient level.
- Inspect the inner exception/log output for the original Exception details to distinguish DNS vs TLS vs connection-reset causes.
Example fix
// before: app configured with unreachable authority options.Authority = "https://idp.internal.local"; // after: verify reachability and use correct host/port options.Authority = "https://idp.internal.local:8443"; // ensure DNS + egress + trust chain allow this
Defensive patterns
Strategy: retry
Validate before calling
// Reachability pre-check before invoking provider calls
using var ping = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
var resp = await ping.GetAsync(new Uri(new Uri(authority), "/.well-known/openid-configuration"));
if (!resp.IsSuccessStatusCode) throw new InvalidOperationException("Provider authority unreachable"); Try / catch
try
{
await providerClient.GetAsync(url, ProviderResponseKind.Discovery);
}
catch (ProviderHttpException pex) when (pex.Failure == ProviderHttpFailure.TransportFailure)
{
logger.LogWarning(pex, "Transport failure contacting provider; will retry");
// retry with backoff or fail over to alternate authority
} Prevention
- Health-check the provider authority at startup and before critical flows.
- Configure Polly-style retry with exponential backoff on the underlying HttpClient.
- Ensure container/pod egress rules and DNS allow the provider host.
- Install the correct CA certificates when TLS interception proxies are present.
When it happens
Trigger: SendAsync (invoked via GetAsync or PostFormAsync) encounters an unexpected Exception during request transmission or response read while the caller's cancellationToken is NOT cancelled — e.g., HttpClient throws HttpRequestException on connection failure, TLS handshake error, or socket reset.
Common situations: Identity provider host unreachable (wrong authority URL, DNS failure), firewall/proxy blocking egress from a container or Kubernetes pod, TLS certificate issues (self-signed certs, expired CA), transient network blips in clustered deployments, or IPv6/IPv4 resolution problems.
Related errors
- The identity provider metadata could not be resolved.
- The identity provider signing keys could not be resolved.
- Timeout
- ResponseTooLarge
- RequestBodyTooLargeException
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/a349a35687e8a34a.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ProviderHttpClientFactory.cs:126
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);
await using var input = await response.Content.ReadAsStreamAsync(cancellationToken);
await using var output = new MemoryStream();
var buffer = new byte[81920];
while (true)
{
var read = await input.ReadAsync(buffer, cancellationToken);
if (read == 0)
return output.ToArray();View on GitHub (pinned to fe9217bdfa)