nats-io/nats-server · error
error decrypting auth callout response on account %q: %v
Error message
error decrypting auth callout response on account %q: %v
What it means
The server received an auth callout response that should be encrypted (because the request was encrypted with the account's xkey) but failed to decrypt it using the xkey key pair. The raw encrypted payload could not be opened, so the authorization decision cannot be read.
Source
Thrown at server/auth_callout.go:110
decodeResponse := func(rc *client, rmsg []byte, acc *Account) (*jwt.UserClaims, error) {
account := acc.Name
_, msg := rc.msgParts(rmsg)
// This signals not authorized.
// Since this is an account subscription will always have "\r\n".
if len(msg) <= LEN_CR_LF {
return nil, fmt.Errorf("auth callout violation: %q on account %q", "no reason supplied", account)
}
// Strip trailing CRLF.
msg = msg[:len(msg)-LEN_CR_LF]
encrypted := false
// If we sent an encrypted request the response could be encrypted as well.
// we are expecting the input to be `eyJ` if it is a JWT
if xkp != nil && len(msg) > 0 && !bytes.HasPrefix(msg, []byte(jwtPrefix)) {
var err error
msg, err = xkp.Open(msg, pubAccXKey)
if err != nil {
return nil, fmt.Errorf("error decrypting auth callout response on account %q: %v", account, err)
}
encrypted = true
}
cr, err := jwt.DecodeAuthorizationResponseClaims(string(msg))
if err != nil {
return nil, err
}
vr := jwt.CreateValidationResults()
cr.Validate(vr)
if len(vr.Issues) > 0 {
return nil, fmt.Errorf("authorization response had validation errors: %v", vr.Issues[0])
}
// the subject is the user id
if cr.Subject != pub {
return nil, errors.New("auth callout violation: auth callout response is not for expected user")
}View on GitHub (pinned to 3a66a489d2)
Solutions
- Ensure the callout service encrypts responses with the account's current public xkey published on the account claims.
- Rotate keys coherently: after changing the account xkey, restart/redeploy the callout service with the new key.
- Log the crypto error detail in the wrapped %v to identify mismatched-key vs corrupted-data cases.
- Verify the response is not raw plaintext; it must be encrypted or a JWT starting with 'eyJ'.
Example fix
// before (callout service)
nc.Publish(msg.Reply, []byte("denied: bad token"))
// after
sealed, _ := xkey.Seal([]byte(respJWT), accPubXKey, svcXKey)
nc.Publish(msg.Reply, sealed) Defensive patterns
Strategy: try-catch
Validate before calling
// in the auth callout service: confirm the account's public xkey before sealing
accJWT, _ := fetchAccountJWT(accPubKey)
accClaims, _ := jwt.DecodeAccountClaims(accJWT)
if len(accClaims.EncryptionKeys) == 0 { return errors.New("no xkey on account; do not encrypt") } Type guard
func hasXKey(ac *jwt.AccountClaims) bool { return len(ac.EncryptionKeys) > 0 } Try / catch
user, err := s.lookupAccountForClientRequest(...)
if err != nil && strings.Contains(err.Error(), "error decrypting auth callout response") {
log.Printf("callout response decryption failed: %v", err)
return nil, ErrAuthentication
} Prevention
- Keep the callout service's copy of the account xkey in sync with rotations
- Only encrypt responses when the request was encrypted
- Log the wrapped crypto error to distinguish key mismatch vs corruption
- Test callout encryption round-trip in CI
When it happens
Trigger: During response handling, when the request was sent with xkp != nil and the response does not start with the JWT prefix 'eyJ', the library attempts xkp.Open(msg, pubAccXKey). Any decryption failure (wrong key, corrupted payload, non-XKey data) returns this error wrapping the crypto failure and the account name.
Common situations: Auth callout service encrypting the response with the wrong public xkey (rotated or mismatched keys), sending plaintext that doesn't begin with 'eyJ', payload truncated by middleware, or key rotation on the account without restarting the callout service.
Related errors
- auth callout violation: %q on account %q
- account jwt not found
- auth callout violation: auth callout response is not for exp
- auth callout violation: auth callout response is not for ser
- auth callout signing key is unknown
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/c00e02996bfb2b3b.
Report an issue: GitHub.