{"record":{"id":"3ab70a53be8c0fc0","repo":"openzipkin/zipkin","slug":"expected-json-proto3-or-thrift-list-encoding","errorCode":null,"errorMessage":"Expected json, proto3 or thrift list encoding","messagePattern":"Expected json, proto3 or thrift list encoding","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/SpanBytesDecoderDetector.java","lineNumber":78,"sourceCode":"    }\n    return decoder;\n  }\n\n  /**\n   * Throws {@link IllegalArgumentException} if the input isn't a json, proto3 or thrift list\n   * message.\n   */\n  public static BytesDecoder<Span> decoderForListMessage(byte[] spans) {\n    return decoderForListMessage(ByteBuffer.wrap(spans));\n  }\n\n  public static BytesDecoder<Span> decoderForListMessage(ByteBuffer spans) {\n    BytesDecoder<Span> decoder = detectDecoder(spans);\n    byte first = spans.get(spans.position());\n    if (first != 12 /* List[ThriftSpan] */\n        && first != 11 /* openzipkin/zipkin-reporter-java#133 */\n        && !protobuf3(spans) && first != '[') {\n      throw new IllegalArgumentException(\"Expected json, proto3 or thrift list encoding\");\n    }\n    return decoder;\n  }\n\n  /** @throws IllegalArgumentException if the input isn't a json or thrift list or object. */\n  static BytesDecoder<Span> detectDecoder(ByteBuffer bytes) {\n    byte first = bytes.get(bytes.position());\n    if (first <= 16) { // binary format\n      if (protobuf3(bytes)) return SpanBytesDecoder.PROTO3;\n      return SpanBytesDecoder.THRIFT; /* the first byte is the TType, in a range 0-16 */\n    } else if (first != '[' && first != '{') {\n      throw new IllegalArgumentException(\"Could not detect the span format\");\n    }\n    if (contains(bytes, ENDPOINT_FIELD_SUFFIX)) return SpanBytesDecoder.JSON_V2;\n    if (contains(bytes, TAGS_FIELD)) return SpanBytesDecoder.JSON_V2;\n    return SpanBytesDecoder.JSON_V1;\n  }\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/SpanBytesDecoderDetector.java#L60-L96","documentation":"SpanBytesDecoderDetector.decoderForListMessage(ByteBuffer) throws IllegalArgumentException('Expected json, proto3 or thrift list encoding') when the first byte does not identify a list container: not 12 (Thrift List[ThriftSpan]), not 11 (legacy thrift empty-list, zipkin-reporter-java#133), not '[', and not a protobuf3 message. It expects payloads in one of the reporter batch encodings.","triggerScenarios":"Passing a single JSON object ('{' first byte), a raw PROTO3 Span struct that fails the protobuf3 heuristic, or arbitrary/corrupt bytes to decoderForListMessage.","commonSituations":"Collector endpoints receiving mixed payloads (some clients send single objects); truncated or double-compressed payloads (gzip not yet decompressed) whose first byte is garbage; encoding mismatches after upgrading a reporter.","solutions":["Ensure the payload is decompressed before detection (gunzip when Content-Encoding is gzip).","Produce the payload with a reporter's list encoder (JSON_V2.encodeList / PROTO3 / Thrift) rather than concatenating raw objects.","For mixed-shape endpoints, branch on the first byte: '{' -> decoderForMessage path, '[' / 12 / 11 -> list path."],"exampleFix":"// before\nBytesDecoder<Span> d = SpanBytesDecoderDetector.decoderForListMessage(raw);\n\n// after\nbyte[] raw = maybeGunzip(body); // handle Content-Encoding first\nBytesDecoder<Span> d = raw[0] == '{'\n    ? SpanBytesDecoderDetector.decoderForMessage(raw)\n    : SpanBytesDecoderDetector.decoderForListMessage(raw);","handlingStrategy":"try-catch","validationCode":"byte first = spans.get(spans.position());\nif (first != '[' && first != '{' && first > 16 && first != 11 && first != 12) {\n  // reject early: unsupported or undecoded (compressed?) payload\n}","typeGuard":null,"tryCatchPattern":"try {\n  decoder = SpanBytesDecoderDetector.decoderForListMessage(buf);\n} catch (IllegalArgumentException e) {\n  // fall back to single-message decoding for '{' payloads, otherwise drop + metric\n  decoder = SpanBytesDecoderDetector.decoderForMessage(bytes);\n}","preventionTips":["Decompress (gzip) before format detection.","Branch on the first byte to support mixed single/list ingestion endpoints."],"tags":["zipkin","codec","decode","validation"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}