hashicorp/nomad · error
unexpected ExpirationLeeway type: %v
Error message
unexpected ExpirationLeeway type: %v
What it means
During JSON unmarshalling of ACLAuthMethod (custom UnmarshalJSON), ExpirationLeeway accepts only a string duration ("5m") or a JSON number (float64, treated as nanoseconds); any other JSON type (bool, object, array, null-like leftover) hits the default branch and returns this error.
Source
Thrown at nomad/structs/acl.go:1678
*Alias
}{
Alias: (*Alias)(a),
}
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.ExpirationLeeway != nil {
switch v := aux.ExpirationLeeway.(type) {
case string:
if v != "" {
if a.ExpirationLeeway, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
a.ExpirationLeeway = time.Duration(v)
default:
return fmt.Errorf("unexpected ExpirationLeeway type: %v", v)
}
}
if aux.NotBeforeLeeway != nil {
switch v := aux.NotBeforeLeeway.(type) {
case string:
if v != "" {
if a.NotBeforeLeeway, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
a.NotBeforeLeeway = time.Duration(v)
default:
return fmt.Errorf("unexpected NotBeforeLeeway type: %v", v)
}
}
if aux.ClockSkewLeeway != nil {
switch v := aux.ClockSkewLeeway.(type) {View on GitHub (pinned to 482b49bf1a)
Solutions
- Send ExpirationLeeway as a Go duration string like "5m" or "1h30m".
- Or send a bare JSON number interpreted as nanoseconds (300000000000 for 5m).
- Fix the client/SDK serialization to emit a scalar for this field.
Example fix
// before
{"ExpirationLeeway": {"minutes": 5}}
// after
{"ExpirationLeeway": "5m"} Defensive patterns
Strategy: validation
Validate before calling
switch v := raw["ExpirationLeeway"].(type) {
case string, float64:
// ok
default:
return fmt.Errorf("ExpirationLeeway must be a duration string or number, got %T", v)
} Type guard
func isDurationScalar(v interface{}) bool {
switch v.(type) {
case string, float64, float32, int64:
return true
}
return false
} Try / catch
if err := json.Unmarshal(payload, &am); err != nil {
if strings.Contains(err.Error(), "unexpected ExpirationLeeway type") {
return fmt.Errorf("send ExpirationLeeway as e.g. \"5m\" or a nanosecond number")
}
return err
} Prevention
- Always encode leeways as Go duration strings ('5m', '1h30m') in JSON
- Reject nested duration objects in config renderers
- Number form is nanoseconds — convert seconds deliberately
When it happens
Trigger: Posting auth-method JSON where ExpirationLeeway is e.g. true, an object {"seconds":300}, or an array — anything that is neither a string nor number.
Common situations: Hand-writing JSON config and quoting wrong / nesting durations; tools emitting YAML that maps to maps rather than scalar durations; forgetting Go's duration-string format and using "5 minutes".
Related errors
- unexpected NotBeforeLeeway type: %v
- unexpected ExpirationLeeway type: %v
- unexpected NotBeforeLeeway type: %v
- unexpected ClockSkewLeeway type: %v
- ACL policy not found
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/eecc679b779f6d00.
Report an issue: GitHub.