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

  1. No action needed: callers treat this error as 'value is not a timestamp' and leave the string as-is.
  2. 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.
  3. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/23b53af4a7ee01ec. Report an issue: GitHub.