anomalyco/sst · error

expect end of JSON object but got more token: %T

Error message

expect end of JSON object but got more token: %T: %v or err: %v

What it means

Once the closing '}' is consumed, UnmarshalJSON requires the decoder to be at EOF. If another token remains (or a non-EOF error exists), it reports 'expect end of JSON object but got more token'. This rejects concatenated or trailing garbage after the object.

Solutions

  1. Feed exactly one JSON document per Unmarshal call — split NDJSON by lines first
  2. Trim trailing whitespace/garbage from the payload
  3. Use a json.Decoder loop over multiple documents if the stream intentionally holds several objects

Example fix

// before
dec := json.NewDecoder(conn)
var kv KeyValuePairs[string]
json.Unmarshal(buf, &kv) // buf has two concatenated objects
// after
lines := bytes.Split(buf, []byte("\n"))
for _, l := range lines {
    var kv KeyValuePairs[string]
    json.Unmarshal(l, &kv)
}
Defensive patterns

Strategy: validation

Validate before calling

dec := json.NewDecoder(bytes.NewReader(raw))
if err := dec.Decode(&json.RawMessage{}); err != nil {
    return err
}
if dec.More() {
    return fmt.Errorf("trailing data after first JSON document")
}

Try / catch

var kv KeyValuePairs[string]
if err := json.Unmarshal(raw, &kv); err != nil {
    if strings.Contains(err.Error(), "expect end of JSON object") {
        return fmt.Errorf("multiple/trailing JSON documents; split NDJSON first")
    }
    return err
}

Prevention

When it happens

Trigger: Input with trailing data after the object, e.g. '{"a":1}{"b":2}' (JSON concatenation/streaming), trailing commas parsed oddly, or appended newline-separated records.

Common situations: NDJSON files fed to json.Unmarshal one blob at a time; log collectors concatenating JSON docs; copy-paste errors leaving stray characters after the object.

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 anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/81cdd25be310d623. Report an issue: GitHub.

Appendix: source

Thrown at internal/util/kv.go:62

		}
		*p = append(*p, KeyValuePair[T]{Key: key, Value: value})
	}

	// must end with a delim token '}'
	t, err = dec.Token()
	if err != nil {
		return err
	}
	if delim, ok := t.(json.Delim); !ok || delim != '}' {
		return fmt.Errorf("expect JSON object close with '}'")
	}
	if err != nil {
		return err
	}

	t, err = dec.Token()
	if err != io.EOF {
		return fmt.Errorf("expect end of JSON object but got more token: %T: %v or err: %v", t, t, err)
	}

	return nil

}
func (p KeyValuePairs[T]) MarshalJSON() ([]byte, error) {
	buf := &bytes.Buffer{}
	buf.Write([]byte{'{'})
	for i, KeyValuePair := range p {
		buf.WriteString(fmt.Sprintf("%q:", fmt.Sprintf("%v", KeyValuePair.Key)))
		encoder := json.NewEncoder(buf)
		encoder.SetEscapeHTML(false)
		err := encoder.Encode(KeyValuePair.Value)
		if err != nil {
			return nil, err
		}
		if i < len(p)-1 {
			buf.Write([]byte{','})

View on GitHub (pinned to a0bd20f762)