openzipkin/zipkin · error · IllegalArgumentException

Incomplete annotation at {}

Error message

Incomplete annotation at {}

What it means

While reading a V1 (legacy) JSON span, each annotation object must contain both 'timestamp' and 'value'. V1JsonSpanReader throws 'Incomplete annotation at <jsonPath>' after finishing the annotation object when either is missing — the JSON parsed fine but the model contract was violated.

Source

Thrown at zipkin/src/main/java/zipkin2/internal/V1JsonSpanReader.java:107

    String nextName;
    reader.beginObject();
    Long timestamp = null;
    String value = null;
    Endpoint endpoint = null;
    while (reader.hasNext()) {
      nextName = reader.nextName();
      if (nextName.equals("timestamp")) {
        timestamp = reader.nextLong();
      } else if (nextName.equals("value")) {
        value = reader.nextString();
      } else if (nextName.equals("endpoint") && !reader.peekNull()) {
        endpoint = ENDPOINT_READER.fromJson(reader);
      } else {
        reader.skipValue();
      }
    }
    if (timestamp == null || value == null) {
      throw new IllegalArgumentException("Incomplete annotation at " + reader.getPath());
    }
    reader.endObject();
    builder.addAnnotation(timestamp, value, endpoint);
  }

  @Override public String toString() {
    return "Span";
  }

  void readBinaryAnnotation(JsonReader reader) throws IOException {
    String key = null;
    Endpoint endpoint = null;
    Boolean booleanValue = null;
    String stringValue = null;

    reader.beginObject();
    while (reader.hasNext()) {
      String nextName = reader.nextName();

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Locate the annotation via the reported JSON path and add the missing member (timestamp in microseconds, value as string).
  2. Prefer V2 JSON (SpanBytesEncoder.JSON_V2) for new integrations; V1 is compatibility-only.
  3. If exporting from code, always construct Annotation.create(timestamp, value) so both fields are present.
  4. Sanitize legacy payloads before ingest: filter annotations lacking either key.

Example fix

// before
{"annotations":[{"value":"sr"}]}

// after
{"annotations":[{"timestamp":1541036190000000,"value":"sr"}]}
Defensive patterns

Strategy: validation

Validate before calling

// sanitize V1 json annotations before decode
JsonNode ann = ...; if (ann.has("timestamp") && ann.has("value")) keep(ann); else skip(ann);

Type guard

boolean isCompleteV1Annotation(JsonNode n) { return n.hasNonNull("timestamp") && n.hasNonNull("value"); }

Prevention

When it happens

Trigger: Decoding with SpanBytesDecoder.JSON_V1 a span whose annotations[] entry lacks 'timestamp' or 'value' (e.g. {"value":"sr"} or {"timestamp":...}).

Common situations: Hand-written V1 JSON fixtures; legacy exporters (finagle, brave pre-3.x formats) that omit timestamps on some annotations; ETL pipelines stripping null fields and dropping a zero timestamp.

Related errors


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