Client Library Bugs Found by QIT 2.0

Bugs and issues discovered during AMQP interoperability testing that should be investigated and reported upstream. Each entry includes a minimal reproducer.

Language limitations (Java/C# 16-bit char, JavaScript Number precision) are excluded — those are inherent to the platform, not library bugs.


1. ProtonJ2: ListTypeEncoder NullPointerException on null-after-non-null

Library: Apache Qpid ProtonJ2 (Java) Severity: Crash (sender cannot send valid AMQP list) AMQP spec: A list MAY contain null elements. A list containing [string, null] is valid.

When a list body contains a null element after a non-null element, the ListTypeEncoder throws a NullPointerException because it tries to look up the encoder for the null element‘s type after having cached the previous element’s encoder.

// repro: ProtonJ2ListNullBug.java
// compile: javac -cp protonj2-client-1.0.0-M22.jar ProtonJ2ListNullBug.java
// run:     java -cp .:protonj2-client-1.0.0-M22.jar ProtonJ2ListNullBug
import org.apache.qpid.protonj2.client.*;
import java.util.*;

public class ProtonJ2ListNullBug {
    public static void main(String[] args) throws Exception {
        Client client = Client.create();
        Connection conn = client.connect("localhost", 5672,
            new ConnectionOptions().user("artemis").password("artemis"));
        Sender sender = conn.openSender("test.list.null.bug");

        List<Object> body = new ArrayList<>();
        body.add("hello");
        body.add(null);  // null after non-null triggers NPE

        Message<List<Object>> msg = Message.create(body);
        sender.send(msg);  // throws NullPointerException in ListTypeEncoder
        conn.close();
    }
}

2. Proton .NET: ListTypeEncoder NullReferenceException on null-after-non-null

Library: Apache Qpid Proton .NET Severity: Crash (sender cannot send valid AMQP list) AMQP spec: Same as above — null elements in lists are valid.

Same root cause as the ProtonJ2 bug: the .NET ListTypeEncoder crashes when encoding a null element that follows a non-null element.

// repro: ListNullBug.cs
// run: dotnet run
using Apache.Qpid.Proton.Client;

var client = IClient.Create();
var options = new ConnectionOptions { User = "artemis", Password = "artemis" };
using var conn = client.Connect("localhost", 5672, options);
using var sender = conn.OpenSender("test.list.null.bug");

var msg = IMessage<object>.Create();
msg.Body = new List<object> { "hello", null };  // null after non-null triggers NRE
sender.Send(msg);  // throws NullReferenceException in ListTypeEncoder

3. ProtonJ2: Null character (U+0000) encoding error

Library: Apache Qpid ProtonJ2 (Java) Severity: Data corruption (sends wrong codepoint) AMQP spec: The char type is a single Unicode character (any valid codepoint including U+0000).

When ProtonJ2 sends a char value of U+0000 (null character), the receiver gets a different codepoint. The encoding path appears to special-case or skip the zero value.

// repro: ProtonJ2CharBug.java
import org.apache.qpid.protonj2.client.*;

public class ProtonJ2CharBug {
    public static void main(String[] args) throws Exception {
        Client client = Client.create();
        Connection conn = client.connect("localhost", 5672,
            new ConnectionOptions().user("artemis").password("artemis"));
        Sender sender = conn.openSender("test.char.null.bug");

        // Send U+0000 as a char
        Message<Character> msg = Message.create('�');
        sender.send(msg);
        // Receiver (any client) gets a different codepoint than U+0000
        conn.close();
    }
}

4. ProtonJ2: Unsigned long values > 2^63 decoded as negative signed long

Library: Apache Qpid ProtonJ2 (Java) Severity: Data corruption (value changes sign) AMQP spec: ulong is an unsigned 64-bit integer (0 to 2^64-1).

ProtonJ2 decodes AMQP ulong values greater than Long.MAX_VALUE (2^63-1) as negative Java long values, because Java has no unsigned 64-bit integer type and the library does not compensate.

// repro: ProtonJ2UlongBug.java
// Send from Python:
//   python3 shim.py send --broker amqp://localhost:5672 --queue test.ulong.bug \
//     --type ulong --count 1 --data '[{"index":0, "value":"18446744073709551615"}]'
// Receive with Java ProtonJ2:
//   java ProtonJ2UlongBug
// Expected: 18446744073709551615 (ULONG_MAX)
// Actual:   -1 (signed interpretation)
import org.apache.qpid.protonj2.client.*;

public class ProtonJ2UlongBug {
    public static void main(String[] args) throws Exception {
        Client client = Client.create();
        Connection conn = client.connect("localhost", 5672,
            new ConnectionOptions().user("artemis").password("artemis"));
        Receiver receiver = conn.openReceiver("test.ulong.bug");
        Delivery delivery = receiver.receive(10_000);
        Object body = delivery.message().body();
        System.out.println("Received: " + body);
        // Prints -1 instead of 18446744073709551615
        conn.close();
    }
}

5. ProtonJ2: timestamp decoded as Long instead of Date

Library: Apache Qpid ProtonJ2 (Java) Severity: Type loss (correct value, wrong Java type) AMQP spec: The timestamp type represents an absolute point in time (ms since Unix epoch).

When receiving an AMQP timestamp value, ProtonJ2 returns a java.lang.Long instead of a java.util.Date (or similar temporal type). The raw millisecond value is correct, but the type information is lost — the receiver cannot distinguish a timestamp from a plain long.

// Receive a message containing an AMQP timestamp:
Delivery delivery = receiver.receive(10_000);
Object body = delivery.message().body();
System.out.println(body.getClass().getName());
// Expected: java.util.Date (or similar)
// Actual:   java.lang.Long

6. ProtonJ2: binary/timestamp decoded incorrectly in list context

Library: Apache Qpid ProtonJ2 (Java) Severity: Data corruption / type loss AMQP spec: Lists may contain elements of any AMQP type, including binary and timestamp.

When binary or timestamp values appear as elements within an AMQP list, ProtonJ2 decodes them incorrectly. Binary data may be returned as a String (UTF-8 interpretation), and timestamp may lose its temporal type.

// Send from Python:
//   List body = [b'\x00\x01\x02', timestamp(1234567890000)]
// Receive with ProtonJ2:
//   List elements are decoded with wrong types

7. ProtonJ2: cannot send binary correlation IDs

Library: Apache Qpid ProtonJ2 (Java) Severity: Missing feature (JMS spec compliance) JMS spec: JMSCorrelationID can be a byte array via setJMSCorrelationIDAsBytes().

ProtonJ2's message-id type restriction prevents setting a binary (byte[]) value as the correlation ID. The AMQP spec allows message-id (and by extension correlation-id) to be uuid, ulong, binary, or string.

Message<String> msg = Message.create("test");
msg.correlationId(new byte[] {0x01, 0x02, 0x03});
// Throws or silently fails — binary correlation ID not supported

8. Proton .NET: cannot send binary correlation IDs

Library: Apache Qpid Proton .NET Severity: Missing feature (same as ProtonJ2)

Neither byte[] nor IProtonBuffer is accepted by the .NET Proton encoder for the correlation-id field.

var msg = IMessage<object>.Create();
msg.Body = "test";
msg.CorrelationId = new byte[] { 0x01, 0x02, 0x03 };
// Encoder rejects binary — only string/ulong/uuid accepted

9. Proton .NET: timestamp decoded as Int64 instead of DateTime

Library: Apache Qpid Proton .NET Severity: Type loss (correct value, wrong .NET type) AMQP spec: Same as bug #5 — timestamp is a distinct AMQP type.

When receiving an AMQP timestamp, Proton .NET returns System.Int64 (raw milliseconds) instead of System.DateTime or System.DateTimeOffset. The value is correct but the type is lost.

// Receive a message containing an AMQP timestamp:
var delivery = receiver.Receive(TimeSpan.FromSeconds(10));
var body = delivery.Message().Body;
Console.WriteLine(body.GetType().Name);
// Expected: DateTime or DateTimeOffset
// Actual:   Int64

10. Proton .NET: byte[] encoded as array-of-ubyte instead of binary in lists

Library: Apache Qpid Proton .NET Severity: Data corruption (wrong AMQP type on wire) AMQP spec: binary is a distinct type from array-of-ubyte. They have different type codes.

When a byte[] appears as an element within a list, Proton .NET encodes it as an AMQP array<ubyte> instead of AMQP binary. This is because byte[] implements IList<byte>, and the encoder picks the array path. Other clients decode this as an array of integers rather than a binary blob.

var msg = IMessage<object>.Create();
msg.Body = new List<object> {
    new byte[] { 0x01, 0x02, 0x03 }  // encoded as array<ubyte>, not binary
};
sender.Send(msg);
// Other clients receive [1, 2, 3] (array of ints) instead of b'\x01\x02\x03'

11. Proton .NET: ubyte array decoded as binary

Library: Apache Qpid Proton .NET Severity: Type loss (wrong type on decode) AMQP spec: array-of-ubyte and binary are distinct types.

The inverse of bug #10: when receiving a valid AMQP array<ubyte>, Proton .NET decodes it as byte[] which is then indistinguishable from binary data.


12. Proton .NET: empty binary round-trip loses type

Library: Apache Qpid Proton .NET Severity: Type loss AMQP spec: An empty binary value (zero-length) is valid and distinct from null.

When Proton .NET sends and receives an empty binary (new byte[0]), the round-tripped value comes back as a different type (null or empty string).

var msg = IMessage<object>.Create();
msg.Body = new byte[0];  // empty binary
sender.Send(msg);
// .NET receiver gets null or wrong type instead of empty byte[]

13. Proton .NET: binary encoding incompatible with ProtonJ2 decoder

Library: Apache Qpid Proton .NET (sender) + ProtonJ2 (receiver) Severity: Interop failure (all binary values fail)

When Proton .NET sends binary data, ProtonJ2 cannot decode any of it correctly. This affects all binary test values, not just edge cases. The encoding produced by .NET may use a format or type descriptor that ProtonJ2's binary decoder does not expect.

# Reproduce:
# 1. Send with .NET:  dotnet-shim send --type binary --data '[{"index":0,"value":"AQID"}]' ...
# 2. Receive with Java ProtonJ2: java-shim receive ...
# All binary values fail to decode correctly

14. Rhea: large ulong/long values cause RangeError or silent precision loss

Library: AMQP Rhea (JavaScript) Severity: Data corruption / crash Root cause: JavaScript Number is IEEE 754 double — safe integer range is -(2^53-1) to 2^53-1.

This is partly a language limitation, but the library should use BigInt for 64-bit integer types rather than silently losing precision or throwing RangeError.

For ulong values > 2^53 and long values with |v| > 2^53: the value is silently rounded to the nearest representable double. For some extreme values, a RangeError is thrown during encoding.

// repro: rhea_precision.js
const rhea = require('rhea');
const container = rhea.create_container();

container.once('sendable', (context) => {
    // This value (ULONG_MAX) will be silently corrupted
    context.sender.send({
        body: rhea.types.wrap_ulong(0xFFFFFFFFFFFFFFFFn)
    });
    // JS Number cannot represent this — precision loss or RangeError
});

Summary Table

#LibraryBugSeverity
1ProtonJ2List NPE on null-after-non-nullCrash
2Proton .NETList NRE on null-after-non-nullCrash
3ProtonJ2Char U+0000 encoding errorData corruption
4ProtonJ2ulong > 2^63 decoded as negativeData corruption
5ProtonJ2timestamp decoded as LongType loss
6ProtonJ2binary/timestamp wrong in listsData corruption
7ProtonJ2Cannot send binary correlation IDMissing feature
8Proton .NETCannot send binary correlation IDMissing feature
9Proton .NETtimestamp decoded as Int64Type loss
10Proton .NETbyte[] in lists encoded as arrayData corruption
11Proton .NETubyte array decoded as binaryType loss
12Proton .NETempty binary round-trip type lossType loss
13Proton .NET→ProtonJ2binary encoding incompatibleInterop failure
14Rhea64-bit integer precision lossData corruption