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

  1. 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().
  2. Verify the client's Consul address points at a real Consul server >= 1.6 with ACLs enabled.
  3. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/57d26ec7aa18093c. Report an issue: GitHub.