micro/go-micro · error

Failed to unmarshal object

Error message

Failed to unmarshal object

What it means

Returned by getRecord when json.Unmarshal of the value stored in the KV entry into KeyValueEnvelope fails. The plugin stores records as JSON envelopes ({key,data,metadata}), so any value in the bucket not written by this plugin (or corrupted) cannot be read.

Source

Thrown at store/nats-js-kv/nats.go:402

	}

	n.buckets.Set(kv.Bucket, store)

	return store, nil
}

// getRecord returns the record with the given key from the nats kv store.
func (n *natsStore) getRecord(bucket nats.KeyValue, key string) (*store.Record, bool, error) {
	obj, err := bucket.Get(key)
	if errors.Is(err, nats.ErrKeyNotFound) {
		return nil, false, store.ErrNotFound
	} else if err != nil {
		return nil, false, errors.Wrap(err, "Failed to get object from bucket")
	}

	var kv KeyValueEnvelope
	if err := json.Unmarshal(obj.Value(), &kv); err != nil {
		return nil, false, errors.Wrap(err, "Failed to unmarshal object")
	}

	if obj.Operation() != nats.KeyValuePut {
		return nil, false, nil
	}

	return &store.Record{
		Key:      kv.Key,
		Value:    kv.Data,
		Metadata: kv.Metadata,
	}, true, nil
}

func (n *natsStore) natsKeys(bucket nats.KeyValue, table, key string, prefix, suffix bool) ([]string, error) {
	if !suffix && !prefix {
		return []string{n.NatsKey(table, key)}, nil
	}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Write all data through store.Write so entries are valid KeyValueEnvelope JSON
  2. If manual values exist in the bucket, re-ingress them: read raw and rewrite with store.Write (json envelope {"key":...,"data":...,"metadata":...})
  3. Check that the value is not empty/corrupted: 'nats kv get <bucket> <key>' and validate the JSON
  4. Use a separate bucket for data written by other tools instead of sharing one

Example fix

// before
nats kv put mybucket mykey --val "raw string"
st.Read("mykey") // -> Failed to unmarshal object
// after
st.Write(&store.Record{Key: "mykey", Value: []byte("raw string")})
st.Read("mykey") // ok
Defensive patterns

Strategy: validation

Validate before calling

raw, err := json.Marshal(rec)
_ = raw
if json.Valid([]byte(raw)) { /* will be stored as envelope by Write */ }

Type guard

func isValidEnvelope(b []byte) bool {
    var kv KeyValueEnvelope
    return json.Unmarshal(b, &kv) == nil
}

Try / catch

_, err := st.Read(key)
if err != nil && strings.Contains(err.Error(), "Failed to unmarshal object") {
    // value was written outside this plugin; re-ingest or delete it
    return fmt.Errorf("bucket contains foreign data at key %s", key)
}

Prevention

When it happens

Trigger: Calling store.Read(key) on a bucket entry that was written directly via NATS (nats kv put / another application) rather than through this store's Write, or whose envelope bytes were corrupted or truncated.

Common situations: Mixing go-micro store plugin access with manual 'nats kv put' writes of raw values; reading a bucket populated by a different tool with a different serialization format; manual edits or partial writes to the JetStream stream.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/107b6e2c327be7e2. Report an issue: GitHub.