openzipkin/zipkin · error · IllegalArgumentException

Not a valid JSON object, start token: {}

Error message

Not a valid JSON object, start token: {}

What it means

parseEndpoint decodes the localEndpoint/remoteEndpoint field of a span and requires it to be a JSON object; a non-object start token throws IllegalArgumentException naming the token. Fields inside (serviceName, ipv4, ipv6, port) are then read; an object with all-null/zero values returns null.

Source

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

          }
          break;
        case "debug":
          result.debug(parser.getBooleanValue());
          break;
        case "shared":
          result.shared(parser.getBooleanValue());
          break;
        default:
          // Skip
      }
    }

    return result.build();
  }

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

    String serviceName = null, ipv4 = null, ipv6 = null;
    int port = 0;

    while (parser.nextToken() != JsonToken.END_OBJECT) {
      JsonToken value = parser.nextValue();
      if (value == JsonToken.VALUE_NULL) {
        continue;
      }

      switch (parser.currentName()) {
        case "serviceName":
          serviceName = parser.getText();
          break;
        case "ipv4":
          ipv4 = parser.getText();

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Fix the document: endpoint must be an object like "localEndpoint":{"serviceName":"frontend","ipv4":"10.0.0.1","port":8080}.
  2. Fix the converting pipeline to build the object shape.
  3. Remove/reindex malformed documents.
  4. In Java, set endpoints via Span.Builder.localEndpoint(Endpoint...) so encoding is handled.

Example fix

// before
{"traceId":"abc","id":"def","localEndpoint":"frontend"}
// -> IllegalArgumentException: Not a valid JSON object, start token: VALUE_STRING

// after
{"traceId":"abc","id":"def","localEndpoint":{"serviceName":"frontend"}}
Defensive patterns

Strategy: type-guard

Validate before calling

var span = objectMapper.readTree(spanJson);
for (String f : new String[]{"localEndpoint", "remoteEndpoint"}) {
  JsonNode e = span.get(f);
  if (e != null && !e.isObject()) throw new IOException(f + " must be an object");
}

Type guard

boolean endpointWellFormed(JsonNode span, String field) {
  JsonNode e = span.get(field);
  return e == null || e.isObject();
}

Try / catch

try { Span s = SPAN_PARSER.parse(p); }
catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Not a valid JSON object") && rawJson.contains("Endpoint")) {
    // endpoint field is not an object; fix producer to emit {serviceName,...}
  }
}

Prevention

When it happens

Trigger: A span document where "localEndpoint" or "remoteEndpoint" is a string, array, or number — e.g. "localEndpoint":"frontend" — parsed by SPAN_PARSER.

Common situations: Simplified hand-written fixtures that inline the service name; converters from other formats (OpenTelemetry, Jaeger) emitting endpoint as a scalar; custom writers inserting documents into Zipkin indices.

Related errors


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