elsa-workflows/elsa-core · error · ProviderHttpException
DestinationRejected
DestinationRejected
Error message
ProviderHttpException(ProviderHttpFailure.DestinationRejected)
What it means
SendAsync validates every request destination (including each redirect hop) through OutboundDestinationValidator. If the validator rejects the host, scheme, port, or proxy destination it throws OutboundDestinationException, which SendAsync translates into ProviderHttpException(ProviderHttpFailure.DestinationRejected). This is the library's SSRF/egress-control boundary: the configured provider URL points somewhere the deployment has not approved.
Solutions
- Update the configured provider/authority URL to an approved destination (public HTTPS host on the allowlist) and redeploy.
- Add the provider's host to the outbound-destination allowlist in your ExternalAuthentication egress configuration if the destination is legitimate.
- Inspect where the redirect chain lands (curl -I each hop) and either allowlist the final host or fix the provider's redirect target.
- Check the DNS resolution: if a public hostname resolves to a private IP, fix DNS or update validator policy deliberately — do not disable validation.
Example fix
// before — internal endpoint rejected by destination validation
await client.GetAsync(new Uri("http://localhost:8080/.well-known/openid-configuration"), ProviderResponseKind.Discovery);
// after — approved HTTPS provider host
await client.GetAsync(new Uri("https://idp.example.com/.well-known/openid-configuration"), ProviderResponseKind.Discovery); Defensive patterns
Strategy: validation
Validate before calling
// validate provider URLs against your egress policy before configuring
var uri = new Uri(candidateUrl);
bool isAllowed = uri.Scheme == Uri.UriSchemeHttps
&& allowedHosts.Contains(uri.Host)
&& !IPAddress.TryParse(uri.Host, out var ip) || !IsPrivateIp(ip!);
if (!isAllowed) throw new InvalidOperationException($"Provider URL not on approved egress list: {candidateUrl}"); Type guard
static bool IsApprovedDestination(Uri uri, IReadOnlySet<string> allowedHosts) =>
uri.Scheme == Uri.UriSchemeHttps && allowedHosts.Contains(uri.Host); Try / catch
try
{
var response = await client.GetAsync(discoveryUri, ProviderResponseKind.Discovery, ct);
}
catch (ProviderHttpException ex) when (ex.Failure == ProviderHttpFailure.DestinationRejected)
{
logger.LogError("Destination rejected by outbound validation: {Uri}", discoveryUri);
throw; // do not retry — egress policy rejection is deterministic, not transient
} Prevention
- Never point External Authentication at http:// or private/loopback addresses in production.
- Keep the egress allowlist in configuration-as-code and update it through review whenever a provider host changes.
- After an OIDC provider change, verify all advertised endpoints (issuer, token, userinfo, JWKS) stay within the allowlist.
- Treat DestinationRejected as config error, not transient: do not build automatic retries around it.
When it happens
Trigger: Calling GetAsync/PostFormAsync with a URI whose scheme/host/port is not allowed by OutboundDestinationValidator policy (e.g. non-HTTPS, private/loopback IP, host not on the allowlist), or a redirect hop landing on a disallowed destination; also thrown when a configured proxy URI fails ValidateApprovedProxy checks during a request.
Common situations: Typing an internal/dev provider URL (http://localhost:8080, http://10.x.x.x) into production config where private egress is blocked; OIDC discovery document advertising endpoints on hosts outside the approved egress list; redirect from the provider to a different domain that is not allowlisted; DNS rebinding or a host resolving to a private IP caught by the validator.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- RedirectRejected
- Access denied.
- RequestBodyTooLargeException
- C# workflow expression execution is disabled. Set…
- Python.NET workflow expression execution is disabled. Set…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/6ee3f6d0e22d544b.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ProviderHttpClientFactory.cs:114
if (IsRedirect(response.StatusCode))
{
if (kind is ProviderResponseKind.Token or ProviderResponseKind.UserInfo || response.Headers.Location is null || redirects++ >= options.Value.ProviderEgress.MaximumRedirects)
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);View on GitHub (pinned to fe9217bdfa)