@SuppressWarnings("FutureReturnValueIgnored")
  // TODO: errorprone wants us to check this future before returning, but what would be a sensible
  // check? Say it is somehow canceled, would we take action? Would callback.onError() be redundant?
  HttpResponse validateAndStoreSpans(SpanBytesDecoder decoder, ServiceRequestContext ctx,
    HttpRequest req) {
    CompletableCallback result = new CompletableCallback();

    req.aggregateWithPooledObjects(ctx.eventLoop(), ctx.alloc()).handle((msg, t) -> {
      if (t != null) {
        result.onError(t);
        return null;
      }

      final HttpData requestContent;
      try {
        requestContent = UnzippingBytesRequestConverter.convertRequest(ctx, msg);
      } catch (Throwable t1) {
        propagateIfFatal(t1);
        result.onError(t1);
        return null;
      }

      try (HttpData content = requestContent) {
        // logging already handled upstream in UnzippingBytesRequestConverter where request context exists
        if (content.isEmpty()) {
          result.onSuccess(null);
          return null;
        }

        final ByteBuffer nioBuffer = content.byteBuf().nioBuffer();

        try {
          SpanBytesDecoderDetector.decoderForListMessage(nioBuffer);
        } catch (IllegalArgumentException e) {
          result.onError(new IllegalArgumentException("Expected a " + decoder + " encoded list\n"));
          return null;
        } catch (Throwable t1) {
          result.onError(t1);
          return null;
        }

        SpanBytesDecoder unexpectedDecoder = testForUnexpectedFormat(decoder, nioBuffer);
        if (unexpectedDecoder != null) {
          result.onError(new IllegalArgumentException(
            "Expected a " + decoder + " encoded list, but received: " + unexpectedDecoder + "\n"));
          return null;
        }

        // collector.accept might block so need to move off the event loop. We make sure the
        // callback is context aware to continue the trace.
        Executor executor = ctx.makeContextAware(ctx.blockingTaskExecutor());
        try {
          collector.acceptSpans(nioBuffer, decoder, result, executor);
        } catch (Throwable t1) {
          result.onError(t1);
          return null;
        }
      }

      return null;
    });

    return HttpResponse.from(result);
  }
This synchronously decodes the message so that users can see data errors.
zipkin-server/src/main/java/zipkin2/server/internal/ZipkinHttpCollector.java