hashicorp/nomad · error

failed to decode data into passed object: %v

Error message

failed to decode data into passed object: %v

What it means

boltdd.Bucket.Get wraps a failure of the msgpack decoder when unmarshaling the stored bytes into the caller-supplied obj pointer. The key existed (otherwise a NotFound error is returned) but its stored representation cannot be decoded into the target type. Typically the stored schema and the Go struct have drifted.

Source

Thrown at helper/boltdd/boltdd.go:353

	// New value written, store hash (bucket path map was created above)
	b.bm.setHash(hashKey, hashVal[:])

	return nil

}

// Get value by key from boltdb or return an ErrNotFound error if key not
// found.
func (b *Bucket) Get(key []byte, obj interface{}) error {
	// Get the raw data from the underlying boltdb
	data := b.boltBucket.Get(key)
	if data == nil {
		return NotFound(string(key))
	}

	// Deserialize the object
	if err := codec.NewDecoderBytes(data, structs.MsgpackHandle).Decode(obj); err != nil {
		return fmt.Errorf("failed to decode data into passed object: %v", err)
	}

	return nil
}

// Iterate iterates each key in Bucket b that starts with prefix. fn is called on
// the key and msg-pack decoded value. If prefix is empty or nil, all keys in the
// bucket are iterated.
//
// b must already exist.
func Iterate[T any](b *Bucket, prefix []byte, fn func([]byte, T)) error {
	c := b.boltBucket.Cursor()
	for k, data := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, data = c.Next() {
		var obj T
		if err := codec.NewDecoderBytes(data, structs.MsgpackHandle).Decode(&obj); err != nil {
			return fmt.Errorf("failed to decode data into passed object: %v", err)
		}
		fn(k, obj)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check the wrapped %v decode error for the offending field index/type mismatch.
  2. Verify obj is a non-nil pointer to a type matching the struct originally written with Put.
  3. If the schema changed, migrate old records (decode with the old struct version, re-encode with the new one) or keep msgpack field order/types stable.
  4. Use a versioned payload struct (wrap data in {Version, Body}) so future changes can be handled explicitly.

Example fix

// before
var meta NodeMeta
bucket.Get(key, &meta) // stored layout has field 3 as int64, meta has time.Duration
// after
var meta nodeMetaV1 // legacy struct matching on-disk layout
if err := bucket.Get(key, &meta); err == nil {
    meta2 := upgrade(meta) // migrate to current struct
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure obj is a non-nil pointer of the expected type before calling Get
rv := reflect.ValueOf(obj)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
    return errors.New("Get requires a non-nil pointer")
}

Type guard

func isReadableInto(obj interface{}) bool {
    if obj == nil { return false }
    rv := reflect.ValueOf(obj)
    return rv.Kind() == reflect.Ptr && !rv.IsNil() && rv.Elem().CanSet()
}

Try / catch

var meta NodeMeta
if err := bucket.Get(key, &meta); err != nil {
    if IsNotFound(err) {
        // key absent: create default
    } else if strings.Contains(err.Error(), "failed to decode") {
        // schema drift: log stored bytes hash, run migration path
    }
    return err
}

Prevention

When it happens

Trigger: Calling Get(key, &obj) where the bytes at key were written by an older version with a different struct layout (renamed/retyped fields, changed msgpack field ordering); the stored bytes were written with a different codec/handle; obj is not a pointer or is an incompatible type for the encoded shape.

Common situations: Consul/serf state files written by an older binary read after an upgrade; changing a field's Go type (e.g. string to time.Duration) in a persisted struct; reusing a bucket key for a different type; passing a non-pointer or wrong-typed obj to Get.

Understand the failure class

Related errors


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