openzipkin/zipkin · error · IllegalArgumentException

Malformed: invalid wireType

Error message

Malformed: invalid wireType 

What it means

A protobuf tag's low 3 bits encode the wire type; only values 0 (varint), 1 (64-bit), 2 (length-delimited), 5 (32-bit) are legal in Zipkin's reader (groups 3/7 are unused/obsolete). Proto3Fields.Field.wireType throws 'Malformed: invalid wireType N at byte P' when the key decodes to wire type 3, 4, 6, or 7, meaning the stream is corrupt or not protobuf.

Solutions

  1. Use the reported byte offset to hex-dump the region and confirm whether the stream is proto3 at all.
  2. Check the preceding field's length prefix for off-by-one bugs in any custom encoding path.
  3. Send the payload through a reference protobuf parser (protoc --decode with zipkin2.proto) to locate the first invalid tag.
  4. Fix the producer to use SpanBytesEncoder.PROTO3 instead of hand-rolled serialization.

Example fix

// before
// custom hand-rolled encoder wrote field key 0x1d (wireType 5) for a string

// after
// use the shipped codec so tags are always well-formed
byte[] bytes = SpanBytesEncoder.PROTO3.encode(span);
Defensive patterns

Strategy: try-catch

Try / catch

try { decode(bytes); } catch (IllegalArgumentException e) { if (e.getMessage().contains("invalid wireType")) { quarantine(bytes); return List.of(); } throw e; }

Prevention

When it happens

Trigger: PROTO3 span decoding hits a tag whose key & 0x7 yields an illegal value — usually the reader is misaligned (after a bad length or truncated varint) or the bytes are another format entirely.

Common situations: Non-proto bytes fed to the proto3 decoder; a producer writing wrong varint tags; corrupted blobs in storage; concatenating two encodings without proper framing.

Understand the failure class

Related errors


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

Appendix: source

Thrown at zipkin/src/main/java/zipkin2/internal/Proto3Fields.java:58

      this(key >>> 3, key & (1 << 3) - 1, key);
    }

    Field(int fieldNumber, int wireType, int key) {
      this.fieldNumber = fieldNumber;
      this.wireType = wireType;
      this.key = key;
    }

    static int fieldNumber(int key, int byteL) {
      int fieldNumber = key >>> 3;
      if (fieldNumber != 0) return fieldNumber;
      throw new IllegalArgumentException("Malformed: fieldNumber was zero at byte " + byteL);
    }

    static int wireType(int key, int byteL) {
      int wireType = key & (1 << 3) - 1;
      if (wireType != 0 && wireType != 1 && wireType != 2 && wireType != 5) {
        throw new IllegalArgumentException(
          "Malformed: invalid wireType " + wireType + " at byte " + byteL);
      }
      return wireType;
    }

    static boolean skipValue(ReadBuffer buffer, int wireType) {
      int remaining = buffer.available();
      switch (wireType) {
        case WIRETYPE_VARINT:
          for (int i = 0; i < remaining; i++) {
            if (buffer.readByte() >= 0) return true;
          }
          return false;
        case WIRETYPE_FIXED64:
          return buffer.skip(8) == 8;
        case WIRETYPE_LENGTH_DELIMITED:
          int length = buffer.readVarint32();
          return buffer.skip(length) == length;

View on GitHub (pinned to 878ce2a1fa)