kubernetes/kops · error

unhandled state for json value (%T %q) serialization: %v %q

Error message

unhandled state for json value (%T %q) serialization: %v %q

What it means

After a value token passes the type switch, the writer serializes it according to the current state character. Valid states are '{' (a field name follows in an object), '[' (an array element), and 'F' (the value of a field). Any other state (including state 0, i.e. a bare value at the top level) has no serialization rule, so a JSON value was written where the state machine does not expect one.

Source

Thrown at pkg/jsonutils/streamwriter.go:172

		j.path = append(j.path, fmt.Sprintf("%s", token))
		return j.writeRaw(j.indent + v + ": ")
	case '[':
		if err := j.writeRaw(j.indent + v); err != nil {
			return err
		}
		j.deferred = ",\n"
		return nil
	case 'F':
		j.state = j.state[:len(j.state)-1]
		j.path = j.path[:len(j.path)-1]
		if err := j.writeRaw(v); err != nil {
			return err
		}
		j.deferred = ",\n"
		return nil
	}

	return fmt.Errorf("unhandled state for json value (%T %q) serialization: %v %q", token, v, state, j.state)
}

func (j *JSONStreamWriter) writeRaw(s string) error {
	if j.deferred != "" {
		if _, err := j.out.Write([]byte(j.deferred)); err != nil {
			return err
		}
		j.deferred = ""
	}
	_, err := j.out.Write([]byte(s))
	return err
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Start the token stream with json.Delim('{') or json.Delim('['); wrap bare scalars in an object/array.
  2. Emit alternating field-name then value tokens inside objects.
  3. Use a fresh writer per document and feed tokens in decoder order.

Example fix

// before
w.WriteToken("hello") // top-level scalar, state==0
// after
w.WriteToken(json.Delim('{'))
w.WriteToken("msg")
w.WriteToken("hello")
w.WriteToken(json.Delim('}'))
Defensive patterns

Strategy: validation

Validate before calling

// Only call WriteToken with value tokens while inside an object (after a field name)
// or an array. Reject top-level scalar documents:
first, _ := dec.Token()
if d, ok := first.(json.Delim); !ok || (d != '{' && d != '[') {
	return fmt.Errorf("document must start with an object or array")
}

Type guard

func docStartsWithContainer(first json.Token) bool {
	d, ok := first.(json.Delim)
	return ok && (d == '{' || d == '[')
}

Try / catch

if err := writer.WriteToken(tok); err != nil {
	return fmt.Errorf("cannot serialize token %v in current state: %w", tok, err)
}

Prevention

When it happens

Trigger: Writing a scalar token at top level before any '{' or '[' delimiter (state is 0); writing two field-name strings in a row without a value between them; continuing a writer whose prior stream was aborted.

Common situations: Streaming a bare JSON string/number (valid JSON per RFC 7159) — the writer only supports documents rooted in objects/arrays; misordered hand-driven token sequences.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/937413263e190cd1. Report an issue: GitHub.