hashicorp/nomad · error
no ACL token returned
Error message
no ACL token returned
What it means
ExchangeOneTimeToken() expects a OneTimeTokenExchangeResponse containing the exchanged ACL token. If the PUT succeeds but the decoded response is nil, the client returns this error instead of dereferencing a nil pointer. It indicates the server/proxy returned an empty or unparseable success response.
Source
Thrown at api/acl.go:237
if resp == nil {
return nil, nil, errors.New("no one-time token returned")
}
return resp.OneTimeToken, wm, nil
}
// ExchangeOneTimeToken is used to create a one-time token
func (a *ACLTokens) ExchangeOneTimeToken(secret string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if secret == "" {
return nil, nil, errors.New("missing secret ID")
}
req := &OneTimeTokenExchangeRequest{OneTimeSecretID: secret}
var resp *OneTimeTokenExchangeResponse
wm, err := a.client.put("/v1/acl/token/onetime/exchange", req, &resp, q)
if err != nil {
return nil, nil, err
}
if resp == nil {
return nil, nil, errors.New("no ACL token returned")
}
return resp.Token, wm, nil
}
var (
// errMissingACLRoleID is the generic errors to use when a call is missing
// the required ACL Role ID parameter.
errMissingACLRoleID = errors.New("missing ACL role ID")
// errMissingACLAuthMethodName is the generic error to use when a call is
// missing the required ACL auth-method name parameter.
errMissingACLAuthMethodName = errors.New("missing ACL auth-method name")
// errMissingACLBindingRuleID is the generic error to use when a call is
// missing the required ACL binding rule ID parameter.
errMissingACLBindingRuleID = errors.New("missing ACL binding rule ID")
)
View on GitHub (pinned to 482b49bf1a)
Solutions
- Confirm the one-time secret is still valid (one-time tokens are single-use and expire quickly) and retry with a fresh one from UpsertOneTimeToken().
- Verify the client's Consul address points at a real Consul server >= 1.6 with ACLs enabled.
- Inspect network intermediaries (proxies, gateways) that could return empty 200 responses.
Defensive patterns
Strategy: try-catch
Type guard
func exchangeRespOK(r *api.OneTimeTokenExchangeResponse) bool { return r != nil && r.Token != nil && r.Token.SecretID != "" } Try / catch
tok, _, err := client.ACLTokens().ExchangeOneTimeToken(secret, nil)
if err != nil {
if err.Error() == "no ACL token returned" {
// mint a fresh one-time token and retry once
}
return err
} Prevention
- One-time tokens are single-use: never retry exchange with the same secret after a failure; mint a new one.
- Verify the Consul address points directly at a server, not an intermediary that can return empty bodies.
When it happens
Trigger: Calling ACLTokens().ExchangeOneTimeToken(secret, q) where the server returns a 200 with an empty body, or a middleware/agent in the path strips the response, leaving resp nil after decoding.
Common situations: Request routed to a non-Consul endpoint (wrong address/port) that replies 200 empty; proxy or service mesh stripping bodies; expired one-time secret handled in a way that returns an empty success rather than an error in some setups.
Related errors
- no one-time token returned
- errMissingACLRoleID
- errMissingACLAuthMethodName
- errMissingACLBindingRuleID
- cannot specify ACL role ID
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/57d26ec7aa18093c.
Report an issue: GitHub.