Search code examples
node.jsprotocol-buffersprotobuf-netprotobuf.jsnode-amqplib

Invalid wire type and index out of range errors when consuming a protobuf message from .net with protobufjs on nodejs


I am trying to consume a protobuf message from RMQ on node js. The protobuf message was created with protobuf-net on C#.Net

So for example the c# object looks like this:

    [ProtoContract]
    public class PositionOpenNotification : MessageBase
    {
    [ProtoMember(1)]
    public int PositionID { get; set; }

    [ProtoMember(2)]
    public int InstrumentID { get; set; }
    ..
    ..Down to tag 30

Then it is added to RMQ and we have .net listeners with the same object on the other side to decode it.

But now we want to read the message from nodejs. For this I am using amqplib and protobuf-js on the nodejs side.

I was trying to decode the message using an object with decorators like this:

import { Message, Type, Field } from "protobufjs/light"; 
 
 @Type.d("PositionOpenNotification")
 export class PositionOpenNotification extends Message<PositionOpenNotification> {
 
  @Field.d(1,"int32", "required")
  public PositionID: number;
}

And Decoding like this:

ch.consume(q.queue, function(msg, res) {
            try {
                if(msg.content) {
                    let decoded = PositionOpenNotification.decode( msg.content);
                    console.log(" Received %s", decoded,q.queue);
                }
              } catch (e) {
                 console.log("Error  %s ", e.message)
              }
        }

where ch is the amqplib RMQ channel.

But I always get one of these errors:

invalid wire type 7 at offset 2

invalid wire type 4 at offset 2

invalid wire type 6 at offset 2

index out of range: 237 + 10 > 237

etc

What am I doing wrong?

EDIT:

It looks like I did not take into account the fact that MessageBase(abstract which PositionOpenNotification inherits) is also a ProtoContract and that the data was serialized with length prefix.

So in the end this is what worked:

Add a MessageBase object with the PositionOpenNotification object in it:

@Type.d("MessageBase")
export class MessageBase extends Message<MessageBase> {

  @Field.d(108, PositionOpenNotification)
  public positionOpenNotification: PositionOpenNotification;
}

And then when deserialzing it:

 if(msg.content) {
    var reader = Reader.create(msg.content);
    let decoded = MessageBase.decodeDelimited(reader);
 }

Solution

  • Wire type 7 doesn't exist, so: the error is correct, at least.

    This type of error is usually an indicator that the payload has been corrupted in transit. The most common way of doing this is by treating it as text, and / or (something that is seen very very frequently): running it through an encoding backwards to transmit the binary data over a text protocol. Check that you're not doing this. Basically, you need to get the exact same bytes at both ends; until you have that, nothing else will work. In particular, if you need to transmit binary over a text protocol: base-64 is your friend.

    As a side note: protobuf-net has methods to export the .proto schema for your object model, to make x-plat more convenient. Look for Serializer.GetProto<T>.

    If you have a payload that you are not sure about, you can use https://protogen.marcgravell.com/decode to validate and inspect the binary data.