{"record":{"id":"04a7f05d6105fca9","repo":"hashicorp/nomad","slug":"unable-to-marshal-acl-token-w","errorCode":null,"errorMessage":"unable to marshal ACL token: %w","messagePattern":"unable to marshal ACL token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/consul_hook.go","lineNumber":361,"sourceCode":"func decodeACLToken(b64ACLToken string, token *consulapi.ACLToken) error {\n\tdecodedBytes, err := base64.StdEncoding.DecodeString(b64ACLToken)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to process ACLToken: %w\", err)\n\t}\n\n\tif len(decodedBytes) != 0 {\n\t\tif err := json.Unmarshal(decodedBytes, token); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to unmarshal ACLToken: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc encodeACLToken(token *consulapi.ACLToken) (string, error) {\n\tjsonBytes, err := json.Marshal(token)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to marshal ACL token: %w\", err)\n\t}\n\n\treturn base64.StdEncoding.EncodeToString(jsonBytes), nil\n}\n\n// This function will never return nil, even in case of error\nfunc (rs *resourcesBackend) loadAllocTokens() (map[string]map[string]*consulapi.ACLToken, error) {\n\tallocTokens := map[string]map[string]*consulapi.ACLToken{}\n\n\tts, err := rs.db.GetAllocConsulACLTokens(rs.allocID)\n\tif err != nil {\n\t\treturn allocTokens, err\n\t}\n\n\tvar mErr *multierror.Error\n\tfor _, st := range ts {\n\n\t\ttoken := &consulapi.ACLToken{}","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/consul_hook.go#L343-L379","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\ntoken := deriveTokenSomewhere()\nenc, err := encodeACLToken(token)\n\n// after: validate the token before encoding\nif token == nil || token.SecretID == \"\" {\n\treturn fmt.Errorf(\"invalid ACL token: missing SecretID\")\n}\nenc, err := encodeACLToken(token)","handlingStrategy":"validation","validationCode":"if token == nil {\n\treturn errors.New(\"ACL token is nil; cannot encode\")\n}\nif _, err := json.Marshal(token); err != nil {\n\treturn fmt.Errorf(\"token not encodable: %w\", err)\n}","typeGuard":"func isEncodableToken(t *consulapi.ACLToken) bool {\n\t_, err := json.Marshal(t)\n\treturn t != nil && err == nil\n}","tryCatchPattern":"enc, err := encodeACLToken(token)\nif err != nil {\n\tif strings.Contains(err.Error(), \"marshal ACL token\") {\n\t\t// re-fetch token from Consul instead of reusing the local object\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["consul","acls","json-marshal","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}