{"record":{"id":"747c41057f95d1fb","repo":"openzipkin/zipkin","slug":"incomplete-annotation-at","errorCode":null,"errorMessage":"Incomplete annotation at {}","messagePattern":"Incomplete annotation at (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/JsonSerializers.java","lineNumber":183,"sourceCode":"\n    long timestamp = 0;\n    String value = null;\n\n    while (parser.nextValue() != JsonToken.END_OBJECT) {\n      switch (parser.currentName()) {\n        case \"timestamp\":\n          timestamp = parser.getLongValue();\n          break;\n        case \"value\":\n          value = parser.getValueAsString();\n          break;\n        default:\n          // Skip\n      }\n    }\n\n    if (timestamp == 0 || value == null) {\n      throw new IllegalArgumentException(\"Incomplete annotation at \" + parser.currentToken());\n    }\n    return Annotation.create(timestamp, value);\n  }\n\n  public static final ObjectParser<DependencyLink> DEPENDENCY_LINK_PARSER = parser -> {\n    if (!parser.isExpectedStartObjectToken()) {\n      throw new IllegalArgumentException(\"Expected start of dependency link object but was \"\n        + parser.currentToken());\n    }\n\n    DependencyLink.Builder result = DependencyLink.newBuilder();\n    JsonToken value;\n    while ((value = parser.nextValue()) != JsonToken.END_OBJECT) {\n      if (value == null) {\n        throw new IOException(\"End of input while parsing object.\");\n      }\n      switch (parser.currentName()) {\n        case \"parent\":","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/JsonSerializers.java#L165-L201","documentation":"After parsing an annotation object, if timestamp == 0 or value == null it is considered incomplete and IllegalArgumentException('Incomplete annotation at <token>') is thrown. A timestamp of exactly 0 is treated as missing because real Zipkin timestamps are epoch-microseconds and never 0; a null value means the 'value' field was absent.","triggerScenarios":"An annotation object missing 'timestamp' or 'value', or with timestamp: 0, e.g. {\"value\":\"sr\"} or {\"timestamp\":0,\"value\":\"sr\"}, inside the annotations array of a span being parsed.","commonSituations":"Fixtures written from memory that omit timestamps; converters dropping zero/absent timestamps; edge data where timestamp legitimately underflows to 0.","solutions":["Ensure every annotation has both fields: {\"timestamp\":<epoch-micros>,\"value\":\"...\"} with timestamp > 0.","Fix the producer to skip annotations lacking a timestamp instead of writing incomplete ones.","Clean up or reindex malformed documents already stored.","In Java code, Annotation.create(timestamp, value) requires the same pair — apply the same rule upstream."],"exampleFix":"// before\n{\"annotations\":[{\"value\":\"sr\"}]}\n// -> IllegalArgumentException: Incomplete annotation at null\n\n// after\n{\"annotations\":[{\"timestamp\":1470150004000000,\"value\":\"sr\"}]}","handlingStrategy":"validation","validationCode":"for (JsonNode a : span.path(\"annotations\")) {\n  if (!a.isObject() || a.path(\"timestamp\").asLong(0) == 0 || !a.has(\"value\")) {\n    throw new IOException(\"annotation missing timestamp(>0) or value: \" + a);\n  }\n}","typeGuard":"boolean annotationComplete(JsonNode a) {\n  return a.isObject() && a.path(\"timestamp\").isNumber() && a.path(\"timestamp\").asLong() > 0\n      && a.has(\"value\") && !a.get(\"value\").isNull();\n}","tryCatchPattern":"try { Span s = SPAN_PARSER.parse(p); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Incomplete annotation\")) {\n    // drop/reject the document and fix the producer to always set timestamp and value\n  }\n}","preventionTips":["Every annotation needs timestamp (epoch-micros, > 0) and value.","Drop incomplete annotations in producers instead of writing them.","When porting legacy data, synthesize a timestamp rather than writing 0."],"tags":["elasticsearch","json-parsing","annotation","validation","span"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}