hashicorp/nomad · error
unable to marshal ACL token: %w
Error message
unable to marshal ACL token: %w
What it means
encodeACLToken wraps a json.Marshal failure when serializing a *consulapi.ACLToken before it is base64-encoded and written to disk. Marshal of a plain struct essentially only fails on unsupported values (e.g. invalid fields produced by custom marshalers) or an out-of-memory-ish encoder error, so this is rare. It guarantees the caller setConsulTokens gets a wrapped, identifiable error instead of the bare json error.
Source
Thrown at client/allocrunner/consul_hook.go:361
func decodeACLToken(b64ACLToken string, token *consulapi.ACLToken) error {
decodedBytes, err := base64.StdEncoding.DecodeString(b64ACLToken)
if err != nil {
return fmt.Errorf("unable to process ACLToken: %w", err)
}
if len(decodedBytes) != 0 {
if err := json.Unmarshal(decodedBytes, token); err != nil {
return fmt.Errorf("unable to unmarshal ACLToken: %w", err)
}
}
return nil
}
func encodeACLToken(token *consulapi.ACLToken) (string, error) {
jsonBytes, err := json.Marshal(token)
if err != nil {
return "", fmt.Errorf("unable to marshal ACL token: %w", err)
}
return base64.StdEncoding.EncodeToString(jsonBytes), nil
}
// This function will never return nil, even in case of error
func (rs *resourcesBackend) loadAllocTokens() (map[string]map[string]*consulapi.ACLToken, error) {
allocTokens := map[string]map[string]*consulapi.ACLToken{}
ts, err := rs.db.GetAllocConsulACLTokens(rs.allocID)
if err != nil {
return allocTokens, err
}
var mErr *multierror.Error
for _, st := range ts {
token := &consulapi.ACLToken{}View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the wrapped json error to identify which field of the ACLToken cannot be marshaled
- Re-fetch the token from Consul to get a well-formed *consulapi.ACLToken instead of reusing the local one
- Verify the token came from a supported Consul API version compatible with the Nomad build
- Upgrade Nomad/Consul API library if the struct was modified or versions are mismatched
Example fix
// before
token := deriveTokenSomewhere()
enc, err := encodeACLToken(token)
// after: validate the token before encoding
if token == nil || token.SecretID == "" {
return fmt.Errorf("invalid ACL token: missing SecretID")
}
enc, err := encodeACLToken(token) Defensive patterns
Strategy: validation
Validate before calling
if token == nil {
return errors.New("ACL token is nil; cannot encode")
}
if _, err := json.Marshal(token); err != nil {
return fmt.Errorf("token not encodable: %w", err)
} Type guard
func isEncodableToken(t *consulapi.ACLToken) bool {
_, err := json.Marshal(t)
return t != nil && err == nil
} Try / catch
enc, err := encodeACLToken(token)
if err != nil {
if strings.Contains(err.Error(), "marshal ACL token") {
// re-fetch token from Consul instead of reusing the local object
}
return err
} Prevention
- Only construct ACLToken objects from Consul API responses, not manually
- Keep the consul/api library version aligned with the Nomad version
- Check for nil tokens before encoding
When it happens
Trigger: setConsulToken(s) receives a *consulapi.ACLToken and calls encodeACLToken; json.Marshal(token) returns a non-nil error (e.g. json: unsupported type resulting from a corrupted/nil-embedded token struct or a custom field type without a marshaler).
Common situations: Programmatic callers constructing an ACLToken with unusual types; an accessor returning a nil/corrupt token from Consul's response; running patched Nomad builds that changed the ACLToken struct.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- unable to unmarshal ACLToken: %w
- no one-time token returned
- no ACL token returned
- errMissingACLRoleID
- errMissingACLAuthMethodName
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/04a7f05d6105fca9.
Report an issue: GitHub.