openzipkin/zipkin · error · UnsupportedOperationException
v2 formats should only be used with list messages
Error message
v2 formats should only be used with list messages
What it means
SpanBytesDecoderDetector.decoderForMessage detects the format of a single-span message; if detection identifies JSON_V2 or PROTO3 (v2 formats), it throws UnsupportedOperationException('v2 formats should only be used with list messages'). Zipkin v2 wire formats (JSON_V2, PROTO3) are only defined as list payloads — e.g. a v2 JSON object '{"id":...}' reaches this path when it starts with '{' and contains endpoint/tags markers.
Source
Thrown at zipkin/src/main/java/zipkin2/SpanBytesDecoderDetector.java:59
/**
* Technically, it is possible to have a v2 span with no endpoints. This should catch the case
* where someone reported a tag without reporting the "localEndpoint".
*
* <p>Note: we don't check for annotations as that exists in both v1 and v2 formats.
*/
static final byte[] TAGS_FIELD = {'"', 't', 'a', 'g', 's', '"'};
/**
* Throws {@link IllegalArgumentException} if the input isn't a v1 json or thrift single-span
* message
*/
public static BytesDecoder<Span> decoderForMessage(byte[] span) {
BytesDecoder<Span> decoder = detectDecoder(ByteBuffer.wrap(span));
if (span[0] == 12 /* List[ThriftSpan] */ || span[0] == '[') {
throw new IllegalArgumentException("Expected json or thrift object, not list encoding");
}
if (decoder == SpanBytesDecoder.JSON_V2 || decoder == SpanBytesDecoder.PROTO3) {
throw new UnsupportedOperationException("v2 formats should only be used with list messages");
}
return decoder;
}
/**
* Throws {@link IllegalArgumentException} if the input isn't a json, proto3 or thrift list
* message.
*/
public static BytesDecoder<Span> decoderForListMessage(byte[] spans) {
return decoderForListMessage(ByteBuffer.wrap(spans));
}
public static BytesDecoder<Span> decoderForListMessage(ByteBuffer spans) {
BytesDecoder<Span> decoder = detectDecoder(spans);
byte first = spans.get(spans.position());
if (first != 12 /* List[ThriftSpan] */
&& first != 11 /* openzipkin/zipkin-reporter-java#133 */
&& !protobuf3(spans) && first != '[') {View on GitHub (pinned to 878ce2a1fa)
Solutions
- Switch to decoderForListMessage for v2 payloads and wrap single objects in a one-element array where needed.
- Send/receive v2 spans as lists: JSON '[{...}]' via SpanBytesEncoder.JSON_V2.encodeList, or PROTO3 via its list encoding.
- If you must support v1 single messages, branch by detected decoder before choosing the message vs list API.
Example fix
// before BytesDecoder<Span> d = SpanBytesDecoderDetector.decoderForMessage(bytes); // after // v2 payloads must travel as lists byte[] listBytes = SpanBytesEncoder.JSON_V2.encodeList(Collections.singletonList(span)); List<Span> back = SpanBytesDecoder.JSON_V2.decodeList(listBytes);
Defensive patterns
Strategy: type-guard
Validate before calling
// v2 payloads must be list-encoded; wrap single v2 objects before decode byte[] listBytes = SpanBytesEncoder.JSON_V2.encodeList(spans);
Type guard
boolean isV2Payload(ByteBuffer buf) {
return SpanBytesDecoderDetector.detectDecoder(buf.duplicate())
.equals(SpanBytesDecoder.JSON_V2); // or PROTO3 via decoderForMessage guard
} Try / catch
try {
decoder = SpanBytesDecoderDetector.decoderForMessage(bytes);
} catch (UnsupportedOperationException e) { // v2 single object
decoder = SpanBytesDecoder.JSON_V2; spanList = decoder.decodeList(wrapInArray(bytes));
} Prevention
- Standardize on list encoding for all v2 wire traffic.
- Pin reporter and collector codec versions together.
When it happens
Trigger: Feeding a single JSON_V2 span object or a bare PROTO3-encoded Span to decoderForMessage instead of wrapping it in a list and using decoderForListMessage.
Common situations: Custom transports that deliver one span at a time and reused v1-era decode code; receiving v2 JSON from zipkin-reporter udp/http senders that were assumed to be v1 single objects.
Related errors
- Expected json or thrift object, not list encoding
- Expected json, proto3 or thrift list encoding
- Could not detect the span format
- should be a 1 to 32 character lower-hex string with no pref
- input is not a list
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/d0cd1ffef7ae76cb.
Report an issue: GitHub.