hashicorp/nomad · error · errRPCCodedErrorPrefix
%s%d,%s
Error message
%s%d,%s
What it means
NewErrRPCCoded wraps an error message with a numeric RPC status code using the internal errRPCCodedErrorPrefix ('%s%d,%s'), so HTTP handlers can later decode the code into an HTTP status via CodeFromRPCCodedErr. It is Nomad's mechanism to carry structured status (e.g. 403 ACL denied, 400 bad params) from RPC endpoints to the HTTP layer.
Source
Thrown at nomad/structs/errors.go:211
// unable to determine the version of a node.
func IsErrUnknownNomadVersion(err error) bool {
return err != nil && strings.Contains(err.Error(), errUnknownNomadVersion)
}
// IsErrNodeLacksRpc returns whether error is due to a Nomad server being
// unable to connect to a client node because the client is too old (pre-v0.8).
func IsErrNodeLacksRpc(err error) bool {
return err != nil && strings.Contains(err.Error(), errNodeLacksRpc)
}
func IsErrNoSuchFileOrDirectory(err error) bool {
return err != nil && strings.Contains(err.Error(), "no such file or directory")
}
// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status
// code
func NewErrRPCCoded(code int, msg string) error {
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
}
// NewErrRPCCodedf wraps an RPC error with a code to be converted to HTTP
// status code.
func NewErrRPCCodedf(code int, format string, args ...any) error {
msg := fmt.Sprintf(format, args...)
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
}
// CodeFromRPCCodedErr returns the code and message of error if it's an RPC error
// created through NewErrRPCCoded function. Returns `ok` false if error is not
// an rpc error
func CodeFromRPCCodedErr(err error) (code int, msg string, ok bool) {
if err == nil || !strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix) {
return 0, "", false
}
headerLen := len(errRPCCodedErrorPrefix)View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the embedded code with structs.CodeFromRPCCodedErr(err) and branch on the HTTP-equivalent status
- If code is 401/403, supply a valid Nomad token via SetSecretID/NOMAD_TOKEN
- If ACLs are disabled on the server, stop sending ACL credentials or enable ACLs
- If 400, validate the policy payload (Name, Rules HCL) before submission
Example fix
// before
if _, err := client.ACLPolicies().Upsert(p, nil); err != nil { return err }
// after
if _, err := client.ACLPolicies().Upsert(p, nil); err != nil {
if code, msg, ok := structs.CodeFromRPCCodedErr(err); ok {
return fmt.Errorf("acl rpc failed (code %d): %s", code, msg)
}
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
// go: check ACLs are enabled before ACL RPCs
agentInfo, err := client.Agent().Self()
if err == nil {
cfg := agentInfo.Config.ACLConfig
if cfg != nil && !cfg.Enabled { return errors.New("ACLs are not enabled on this cluster") }
} Type guard
func IsRPCCoded(err error) (int, string, bool) { return structs.CodeFromRPCCodedErr(err) } Try / catch
if _, err := client.ACLPolicies().Upsert(p, nil); err != nil {
if code, msg, ok := structs.CodeFromRPCCodedErr(err); ok {
switch code {
case 401, 403: /* fix token */
default: return fmt.Errorf("rpc %d: %s", code, msg)
}
}
return err
} Prevention
- Always attach a secret token when ACLs are enabled
- Decode coded errors instead of string-matching messages
- Validate policy payloads client-side before writes
When it happens
Trigger: Calling NewErrRPCCoded(code, msg) with a non-200 code; called from ACL policy write/delete paths (UpsertPolicies, DeletePolicies) and via aclDisabled when the agent has ACLs disabled but an authenticated request arrives, or Apply/Profile fail with a coded condition.
Common situations: POSTing ACL policies to a cluster where ACLs are not enabled (aclDisabled path returns a coded 400/401); malformed policy submissions rejected with 400; permission errors surfaced as coded 403s.
Related errors
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/aeba0839eb54b356.
Report an issue: GitHub.