anomalyco/sst · error

expect JSON object open with '{'

Error message

expect JSON object open with '{'

What it means

KeyValuePairs.UnmarshalJSON parses a JSON object incrementally with json.Decoder tokens. If the first token is not the '{' delimiter — i.e. the input is an array, string, number, or malformed — it returns this error. It enforces that the serialized form of KeyValuePairs is a JSON object.

Source

Thrown at internal/util/kv.go:27

type KeyValuePair[T any] struct {
	Key   string
	Value T
}
type KeyValuePairs[T any] []KeyValuePair[T]

func (p *KeyValuePairs[T]) UnmarshalJSON(data []byte) error {
	*p = make(KeyValuePairs[T], 0)
	dec := json.NewDecoder(bytes.NewReader(data))
	dec.UseNumber()

	// must open 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 open with '{'")
	}

	for dec.More() {
		t, err = dec.Token()
		if err != nil {
			return err
		}

		key, ok := t.(string)
		if !ok {
			return fmt.Errorf("expecting JSON key should be always a string: %T: %v", t, t)
		}
		var value T
		err = dec.Decode(&value)
		if err != nil {
			return fmt.Errorf("JSON value can't be decoded: %T: %v", value, value)
		}
		*p = append(*p, KeyValuePair[T]{Key: key, Value: value})

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Ensure the input is a JSON object like {"key":"value"}, not an array
  2. Check the producer/API contract and fix the payload shape
  3. Validate JSON with a parser before unmarshaling to catch truncation

Example fix

// before
var kv KeyValuePairs[string]
json.Unmarshal([]byte(`[{"k":"v"}]`), &kv)
// after
var kv KeyValuePairs[string]
json.Unmarshal([]byte(`{"k":"v"}`), &kv)
Defensive patterns

Strategy: validation

Validate before calling

var probe any
if err := json.Unmarshal(raw, &probe); err != nil {
    return fmt.Errorf("invalid JSON: %w", err)
}
if m, ok := probe.(map[string]any); !ok {
    return fmt.Errorf("expected JSON object, got %T", probe)
}

Type guard

func isJSONObject(raw []byte) bool {
    var m map[string]any
    return json.Unmarshal(raw, &m) == nil && m != nil
}

Try / catch

var kv KeyValuePairs[string]
if err := json.Unmarshal(raw, &kv); err != nil {
    if strings.Contains(err.Error(), "expect JSON object open") {
        return fmt.Errorf("payload must be a JSON object: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Unmarshaling a JSON array (e.g. '[{"k":"v"}]'), a bare string/number, or empty/garbage input into a KeyValuePairs[T] field.

Common situations: API payload changed shape from object to array; feeding a JSON lines file into json.Unmarshal; a producer wrote an array of pairs while the consumer expects an object; corrupted/truncated JSON body.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/b78089ed6fdffe0c. Report an issue: GitHub.