hashicorp/nomad · info
bad length: %d
Error message
bad length: %d
What it means
maybeDecodeTime heuristically converts strings that look like binary msgpack-encoded time.Time values back into time.Time when walking snapshot data. A string that is non-ASCII (so a candidate) but whose length is not 4, 8, or 12 bytes cannot be any known msgpack time encoding, so it returns "bad length: %d". This is normal control flow: fixTime treats the error as 'not a time' and leaves the value untouched.
Source
Thrown at helper/raftutil/msgpack.go:57
// maybeDecodeTime returns a time.Time representation if the string represents a msgpack
// representation of a date.
func maybeDecodeTime(v string) (*time.Time, error) {
if isASCII(v) {
return nil, fmt.Errorf("simple ascii string")
}
tt := &time.Time{}
var err error
err = tt.UnmarshalBinary([]byte(v))
if err == nil {
return tt, nil
}
switch len(v) {
case 4, 8, 12:
default:
return nil, fmt.Errorf("bad length: %d", len(v))
}
var nb bytes.Buffer
err = codec.NewEncoder(&nb, structs.MsgpackHandle).Encode(v)
if err != nil {
return nil, err
}
err = codec.NewDecoder(&nb, structs.MsgpackHandle).Decode(tt)
if err != nil {
return nil, err
}
return tt, nil
}
// isASCII returns true if all string characters are ASCII characters
func isASCII(s string) bool {View on GitHub (pinned to 482b49bf1a)
Solutions
- No action needed: callers treat this error as 'value is not a timestamp' and leave the string as-is.
- If a real time value is being missed, verify the snapshot's msgpack handle matches structs.MsgpackHandle so times encode in the expected 4/8/12-byte form.
- If you call maybeDecodeTime directly, first check len(v) is 4, 8, or 12 to avoid the error entirely.
Defensive patterns
Strategy: validation
Validate before calling
// Only treat strings as candidate times if they are non-ASCII and a valid length
func isCandidateTime(v string) bool {
if isASCII(v) { return false }
switch len(v) { case 4, 8, 12: return true }
return false
} Type guard
// Narrow decoded map values before use
t, ok := val.(time.Time)
if !ok {
if s, isStr := val.(string); isStr && isCandidateTime(s) {
if tt, err := maybeDecodeTime(s); err == nil && isReasonableTime(tt) { t, ok = *tt, true }
}
} Try / catch
if t, err := maybeDecodeTime(s); err == nil && isReasonableTime(t) {
m[k] = *t
} // else: leave the original string; the error is an expected 'not a time' signal Prevention
- Treat this error as normal control flow — never log or fail on it in the fixTime path.
- When calling maybeDecodeTime directly, pre-check len(v) ∈ {4,8,12}.
- Always pair a successful decode with isReasonableTime to avoid absurd dates.
- Ensure snapshots are encoded with structs.MsgpackHandle so times use the expected binary forms.
When it happens
Trigger: fixTime iterates map values decoded from a snapshot and passes any non-ASCII string to maybeDecodeTime; the string fails time.Time.UnmarshalBinary and its byte length is not 4, 8, or 12 (e.g. any non-ASCII UTF-8 string like accented names or emoji).
Common situations: Redacting or inspecting snapshots (RedactSnapshot path) where job/variable/node payloads contain non-ASCII strings that are not timestamps — this is expected and harmless; only a bug if genuinely encoded times are being dropped.
Related errors
- failed to decode task state from 'simple-all' entry: %v
- failed to decode data into passed object: %v
- failed to decode log entry at index %d: %v
- failed to decode HCL file %s: %w
- failed to decode snapshot %q: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/23b53af4a7ee01ec.
Report an issue: GitHub.