microsoft/typescript-go · error
cannot unmarshal non-object JSON value into Map
Error message
cannot unmarshal non-object JSON value into Map
What it means
OrderedMap's UnmarshalJSON (json/v2 decoding) requires the top-level JSON value to be an object; it explicitly allows null as a no-op but rejects arrays, strings, numbers, and booleans with this error. The map's key/value types are then decoded pairwise from the object body.
Source
Thrown at internal/collections/ordered_map.go:276
panic("unexpected map key type")
}
var _ json.UnmarshalerFrom = (*OrderedMap[string, string])(nil)
func (m *OrderedMap[K, V]) UnmarshalJSONFrom(dec *json.Decoder) error {
token, err := dec.ReadToken()
if err != nil {
return err
}
if token.Kind() == 'n' { // json.Null.Kind()
// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
// https://pkg.go.dev/encoding/json#Unmarshaler
// TODO: reconsider
return nil
}
if token.Kind() != '{' { // json.ObjectStart.Kind()
return errors.New("cannot unmarshal non-object JSON value into Map")
}
for dec.PeekKind() != '}' { // json.ObjectEnd.Kind()
var key K
var value V
if err := json.UnmarshalDecode(dec, &key); err != nil {
return err
}
if err := json.UnmarshalDecode(dec, &value); err != nil {
return err
}
m.Set(key, value)
}
if _, err := dec.ReadToken(); err != nil {
return err
}
return nil
}
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Fix the producer to emit a JSON object ({"k": v, ...}) at that position
- If the input is genuinely an array, decode into a slice first and then insert entries into the map
- Pre-validate the payload shape (see validation) before handing it to Unmarshal
Example fix
// before
var m collections.OrderedMap[string, int]
err := m.UnmarshalJSON([]byte(`["a","b"]`)) // cannot unmarshal non-object JSON value into Map
// after
var m collections.OrderedMap[string, int]
err := m.UnmarshalJSON([]byte(`{"a":1,"b":2}`)) Defensive patterns
Strategy: type-guard
Validate before calling
// Pre-validate the top-level JSON shape before decoding into the Map.
func isJSONObject(b []byte) bool {
for _, c := range b {
switch c {
case ' ', '\t', '\r', '\n':
continue
case '{':
return true
default:
return false
}
}
return false
} Type guard
func isNonObjectJSON(err error) bool {
return err != nil && strings.Contains(err.Error(), "cannot unmarshal non-object JSON value into Map")
} Try / catch
var m collections.OrderedMap[string, int]
if err := m.UnmarshalJSON(payload); err != nil {
if isNonObjectJSON(err) {
// Producer sent an array: decode as slice and rebuild the map.
var pairs []struct {
K string `json:"k"`
V int `json:"v"`
}
if jerr := json.Unmarshal(payload, &pairs); jerr == nil {
for _, p := range pairs { m.Set(p.K, p.V) }
} else {
return jerr
}
} else {
return err
}
} Prevention
- Pin the API contract: map fields must always be JSON objects, never arrays
- In tests, assert payload shape (`json.RawMessage` first byte is '{') before decoding into OrderedMap
- Watch for double-encoded JSON (a string containing JSON), which also trips this check
When it happens
Trigger: Decoding a JSON array ('[{...}]') or scalar into an collections.OrderedMap / Map[K,V]; a server changing a map-typed field to a list; wrapping the payload in brackets when pretty-printing or proxying; decoding a JSON string produced by double-marshalling.
Common situations: API contract drift between versions where an object field becomes an array (or vice versa); hand-constructed JSON test fixtures; middleware that re-encodes bodies and accidentally changes the top-level shape.
Related errors
- Found ${unusedBaselines.length} unused baseline file(s). Run
- Failed to extract version from version.go
- DocumentIdentifier: expected string or object, got %v
- failed to unmarshal %T: %w
- %w: %w
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/d7ebafd11dfe6c3b.
Report an issue: GitHub.