nats-io/nats-server · error
auth callout violation: %q on account %q
Error message
auth callout violation: %q on account %q
What it means
The auth callout (decentralized authentication) service on the account responded with an empty authorization response, i.e. a message containing nothing beyond the trailing CRLF. The server treats an empty response as a rejection with no reason and reports this violation naming 'no reason supplied' and the account.
Source
Thrown at server/auth_callout.go:99
xkp, xkey = s.xkp, s.info.XKey
}
// Create a keypair for the user. We will expect this public user to be in the signed response.
// This prevents replay attacks.
ukp, _ := nkeys.CreateUser()
pub, _ := ukp.PublicKey()
reply := s.newRespInbox()
respCh := make(chan string, 1)
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, errView on GitHub (pinned to 3a66a489d2)
Solutions
- Fix the auth callout service to publish a valid signed AuthorizationResponseClaims JWT in the response body.
- Log the raw response in the callout service to find where the empty body originates.
- Ensure the callout service only publishes responses to the reply subject and includes the error/ok decision field.
- If intentional rejection, respond with a properly signed response carrying an error reason instead of an empty message.
Example fix
// before (callout service)
nc.Publish(msg.Reply, []byte("\r\n"))
// after
resp, _ := jwt.EncodeAuthorizationResponseClaims(claims, signerKey)
nc.Publish(msg.Reply, []byte(resp)) Defensive patterns
Strategy: validation
Validate before calling
// in the auth callout service before publishing the response
if len(respBytes) <= 2 {
return errors.New("refusing to publish empty authorization response")
}
nc.Publish(msg.Reply, respBytes) Type guard
func hasBody(msg []byte) bool { return len(msg) > 2 } Try / catch
user, err := s.lookupAccountForClientRequest(...)
if err != nil && strings.Contains(err.Error(), "auth callout violation") {
log.Printf("auth callout returned empty response on %s", account)
return nil, ErrAuthentication
} Prevention
- Always publish a signed AuthorizationResponseClaims from the callout service
- Add a self-test that sends a request and verifies a non-empty response
- Log raw responses in the callout service
- Guard against crash-path handlers publishing empty ACKs
When it happens
Trigger: While processing an authorization request response (rc.msgParts on the reply message), if len(msg) <= LEN_CR_LF the library returns this error. Happens when the auth callout service publishes an empty message body to the access-control reply subject, or sends a response that is only CRLF.
Common situations: Auth callout service bug that replies without serializing the AuthorizationResponseClaims; service crashing and a stub/empty publish being sent; proxy or middleware stripping the payload; test harness replying with an empty ACK.
Related errors
- credentials have been revoked
- no_auth_user not compatible with Trusted Operator
- no_auth_user: "%s" present, but users/nkeys are not defined
- no_auth_user: "%s" not present as user or nkey in authorizat
- error decrypting auth callout response on account %q: %v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/2384480283fceb24.
Report an issue: GitHub.