hashicorp/nomad · error
unexpected TTL type: %v
Error message
unexpected TTL type: %v
What it means
This error is returned by the SetahlTTL-style mapstructure decode hook / UnmarshalJSON helper in HashiCorp Nomad's ACL token struct when the TTL value supplied for an ACL token (e.g. a client token introduction/expiration TTL) arrives as a JSON/encoding type other than the accepted string-with-duration or float64 form. The decoder switches on the Go type of the raw value; float64 is cast to time.Duration nanoseconds, but any other type (bool, nil, string handled elsewhere) falls through to the default branch. It signals that the caller sent a structurally invalid TTL value for the ACL API.
Source
Thrown at nomad/structs/acl.go:2530
*Alias
}{
Alias: (*Alias)(a),
}
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.TTL != nil {
switch v := aux.TTL.(type) {
case string:
if v != "" {
if a.TTL, err = time.ParseDuration(v); err != nil {
return err
}
}
case float64:
a.TTL = time.Duration(v)
default:
return fmt.Errorf("unexpected TTL type: %v", v)
}
}
return nil
}
// ACLCreateClientIntroductionTokenResponse is the response object used within the ACL
// client introduction RPC handler.
type ACLCreateClientIntroductionTokenResponse struct {
// JWT is the signed identity token that can be used as an introduction
// token for a new client node to register with the Nomad cluster.
JWT string
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the payload sent to the ACL API and set the TTL field to a valid duration string (e.g. "30s") or a plain JSON number.
- Check for template/config generation bugs that emit the wrong JSON type (bool/null/object) for the TTL key.
- Upgrade the Nomad client/SDK so the decode hook matches the server's accepted TTL formats.
- If writing custom tooling, parse the value into time.Duration before submitting it.
Example fix
// before
{ "Name": "deploy", "Type": "client", "TTL": true }
// after
{ "Name": "deploy", "Type": "client", "TTL": "30s" } Defensive patterns
Strategy: type-guard
Validate before calling
if v, ok := rawTTL.(string); !ok && !isJSONNumber(rawTTL) {
return fmt.Errorf("TTL must be a duration string or number, got %T", rawTTL)
} Type guard
func validTTL(v interface{}) bool {
switch v.(type) {
case string, float64, int64, int:
return true
default:
return false
}
} Try / catch
if err := token.Validate(); err != nil {
if strings.Contains(err.Error(), "unexpected TTL type") {
// reject payload, log the offending field type and re-serialize with a duration string
}
return err
} Prevention
- Always send TTL as a Go-style duration string ("30s") in JSON payloads.
- Never emit booleans, null, or nested objects for duration fields.
- Marshal durations via time.Duration String() before serialization.
- Add schema validation on client payloads before calling the ACL API.
When it happens
Trigger: Calling the ACL token creation/introduction API (or feeding a Nomad config/JSON blob through structs.DecodeACLToken-style decoding) with a TTL field typed as something other than an accepted duration representation - e.g. a boolean, null, nested object, or array. Typically seen when an automation tool emits the wrong JSON type for the TTL field.
Common situations: Terraform/CLI tooling generating JSON with a wrong-typed TTL (e.g. "ttl": true or an object); template rendering producing quoted numbers where the decoder path doesn't accept strings; SDK clients marshaling Go ints (not float64) into a hook that only handles float64 after JSON decoding.
Related errors
- unexpected ClockSkewLeeway type: %v
- ACL policy not found
- ACL role not found
- detected corrupted token within the state store: missing rol
- ACL binding rule not found
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/5d20152cd28129f6.
Report an issue: GitHub.