openzipkin/zipkin · error · IllegalArgumentException

Not a valid JSON object, start token: %s

Error message

Not a valid JSON object, start token: %s

What it means

JsonSerializers.parseSpan requires the parser to start at a JSON object ({...}) before reading span fields; anything else (array, string, null token from empty input) fails immediately with IllegalArgumentException naming the offending start token. This parser decodes span documents returned from Elasticsearch, so the error means the storage returned a span-shaped position that is not a JSON object.

Source

Thrown at zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/JsonSerializers.java:44

  public static final JsonFactory JSON_FACTORY = new JsonFactory();

  public static JsonGenerator jsonGenerator(OutputStream stream) {
    try {
      return JSON_FACTORY.createGenerator(stream);
    } catch (IOException e) {
      throw new AssertionError("Could not create JSON generator for a memory stream.", e);
    }
  }

  public interface ObjectParser<T> {
    T parse(JsonParser jsonParser) throws IOException;
  }

  public static final ObjectParser<Span> SPAN_PARSER = JsonSerializers::parseSpan;

  static Span parseSpan(JsonParser parser) throws IOException {
    if (!parser.isExpectedStartObjectToken()) {
      throw new IllegalArgumentException("Not a valid JSON object, start token: " +
        parser.currentToken());
    }

    Span.Builder result = Span.newBuilder();

    JsonToken value;
    while ((value = parser.nextValue()) != JsonToken.END_OBJECT) {
      if (value == null) {
        throw new IOException("End of input while parsing object.");
      }
      if (value == JsonToken.VALUE_NULL) {
        continue;
      }
      switch (parser.currentName()) {
        case "traceId":
          result.traceId(parser.getText());
          break;
        case "parentId":

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Inspect the JSON you feed/expect: each span must be a single object starting with '{'.
  2. If data comes from an index, reindex or delete the malformed documents (query by _source shape to find them).
  3. In tests/tools, wrap the parser on the correct node (the object inside hits[]._source), not the outer response.
  4. Align zipkin versions so converters and parsers agree on response structure.

Example fix

// before
JsonParser p = factory.createParser("[{\"traceId\":\"...\"}]"); // array
SPAN_PARSER.parse(p); // Not a valid JSON object, start token: START_ARRAY

// after
JsonParser p = factory.createParser("{\"traceId\":\"...\",\"id\":\"...\"}");
Span span = SPAN_PARSER.parse(p);
Defensive patterns

Strategy: type-guard

Validate before calling

// validate shape before parsing if input provenance is unknown
var node = objectMapper.readTree(spanJson);
if (!node.isObject()) {
  throw new IOException("Span payload must be a JSON object, was: " + node.getNodeType());
}

Type guard

boolean isSpanObjectShape(com.fasterxml.jackson.databind.JsonNode n) {
  return n.isObject() && n.has("traceId") && n.has("id");
}

Try / catch

try {
  Span span = JsonSerializers.SPAN_PARSER.parse(parser);
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Not a valid JSON object")) {
    // skip/reject the malformed record, log the raw payload
  }
}

Prevention

When it happens

Trigger: Search results whose hits or source values are not objects: a malformed index containing non-object documents, a converter feeding the parser at the wrong depth, or manually crafted/consumed JSON tests that pass arrays where span objects are expected.

Common situations: Direct use of JsonSerializers.SPAN_PARSER in tooling/tests; corrupted index data; response-shape mismatch after cluster upgrade; feeding concatenated JSON streams.

Related errors


AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14). Data as JSON: /api/errors/f5034fa7aafd94d6. Report an issue: GitHub.