hashicorp/nomad · error
failed to exchange token with provider: %v
Error message
failed to exchange token with provider: %v
What it means
Thrown by OIDCCompleteAuth when the cap/go-oidc provider's Exchange() call fails to swap the OIDC authorization code (plus signed state) for a provider token. The wrapped provider error is embedded via %v, so the underlying cause (network, TLS, invalid code, client auth) is in the message. This happens server-side in Nomad during the callback step of the OIDC login flow.
Source
Thrown at nomad/acl_endpoint.go:2800
}
// Retrieve the request generated in OIDCAuthURL()
oidcReq := a.oidcRequestCache.LoadAndDelete(args.ClientNonce) // I am so done with this NONCENSE
if oidcReq == nil {
// note: this may happen if there is a leader election between getting
// the auth url and completing the login flow here.
return errors.New("no OIDC request found for client nonce")
}
// Generate a context with a deadline. This is passed to the OIDC provider
// and used when making remote HTTP requests.
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(aclOIDCCallbackRequestExpiryTime))
defer cancel()
// Exchange the state and code for an OIDC provider token.
oidcToken, err := oidcProvider.Exchange(ctx, oidcReq, args.State, args.Code)
if err != nil {
return fmt.Errorf("failed to exchange token with provider: %v", err)
}
if !oidcToken.Valid() {
return errors.New("exchanged token is not valid; potentially expired or empty")
}
var idTokenClaims map[string]any
if err := oidcToken.IDToken().Claims(&idTokenClaims); err != nil {
return fmt.Errorf("failed to retrieve the ID token claims: %v", err)
}
var userClaims map[string]any
if !authMethod.Config.OIDCDisableUserInfo {
if userTokenSource := oidcToken.StaticTokenSource(); userTokenSource != nil {
if err := oidcProvider.UserInfo(ctx, userTokenSource, idTokenClaims["sub"].(string), &userClaims); err != nil {
return fmt.Errorf("failed to retrieve the user info claims: %v", err)
}
}
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Check the wrapped cause in the error message; fix the specific provider complaint (invalid code, invalid client, network).
- Ensure the callback is hit exactly once — auth codes are single-use; restart the login flow if the browser reloaded the callback.
- Verify OIDCClientID, OIDCClientSecret / OIDCClientAssertion (client-id, key-source) in the ACL auth method config match the IdP registration.
- From a Nomad server, verify network/DNS/TLS reachability of the provider's discovery URL.
- Check clock skew on Nomad servers and the IdP (NTP).
Example fix
// before: reusing a stale callback URL curl 'https://nomad/ui/oidc/callback?code=OLD_CODE&state=OLD_STATE' // after: start a fresh login # navigate to https://nomad/ui/settings/tokens and click 'Sign in with SSO' again
Defensive patterns
Strategy: try-catch
Validate before calling
// before calling Login/complete auth, verify config + reachability
for _, u := range []string{method.Config.DiscoveryURL} {
resp, err := http.Get(u + "/.well-known/openid-configuration")
if err != nil { return fmt.Errorf("provider unreachable: %w", err) }
resp.Body.Close()
} Try / catch
token, err := oidcProvider.Exchange(ctx, req, state, code)
if err != nil {
return fmt.Errorf("failed to exchange token with provider: %v", err)
}
// inspect err string for 'invalid_grant' → restart flow; 'connection refused' → check network Prevention
- Never reuse or reload callback URLs — auth codes are single-use; always restart the login flow.
- Validate OIDCClientID/Secret against the IdP registration before saving the auth method.
- Monitor NTP/clock skew on Nomad servers and the IdP.
- Smoke-test provider reachability from every Nomad server after network changes.
When it happens
Trigger: ACL.oidcClient.Exchange(ctx, oidcReq, args.State, args.Code) returns an error: the auth code was already used or expired, the state does not match, the provider is unreachable, or the client id/secret (or client assertion) is wrong.
Common situations: User reloads or double-submits the callback URL (code replay), clock skew between Nomad and the IdP, wrong OIDCClientID/OIDCClientSecret in the auth method config, IdP behind a firewall/DNS failure from the Nomad servers, provider token endpoint rejecting private_key_jwt client assertion.
Related errors
- no signed workload identity available
- no auth method config or client assertion
- missing OIDCDiscoveryURL
- missing OIDCClientID
- missing Audience
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/4d5e48c2477c5b21.
Report an issue: GitHub.