Tonic: Status::with_details not working as expected

Created on 15 Jul 2020  路  10Comments  路  Source: hyperium/tonic

Bug Report

Version

0.3.0

Platform

Darwin moh-abks-mbp.lan 19.5.0 Darwin Kernel Version 19.5.0: Tue May 26 20:41:44 PDT 2020; root:xnu-6153.121.2~2/RELEASE_X86_64 x86_64

Crates

tonic

Description

I'm generating https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto using prost

then creating the below

let response = PreconditionFailure {
   violations: vec! [
      Violation      {
         r#type: e.to_string(),
         subject: s.to_string(),
         description: m.to_string()
      }
   ]
};


let mut response_buffer = BytesMut::with_capacity(4096);
response.encode(&mut response_buffer).expect("encode error");

returning it as part of a tonic error;

Err(Status::with_details(
  Code::FailedPrecondition,
  "Failed Precondition",
  Bytes::from(response_buffer),
))

i can log the error and see this;

error: Status { code: PreconditionFailure, message: "Invalid Argument", details: b"ClwKF2ludmFsaWRfcmVjaXBpZW50X3Bob25lEkFSZWNpcGllbnQgcGhvbmUgbnVtYmVyIGlzIG5vdCB2YWxpZCBmb3IgdGhlIGdpdmVuIG1vYmlsZSBvcGVyYXRvcg" }

on the other end (Golang) when I attempt to get details I get EOF error.

This is my go code to access details

func expandErrorDetails(details[] interface {})[] string {
    for _, detail: = range details {
        switch t: = detail.(type) {
            case *epb.BadRequest:
                for _, violation: = range t.GetFieldViolations() {
                    return [] string {
                        violation.GetField(),
                            violation.GetDescription(),
                    }
                }
            case *epb.PreconditionFailure:
                for _, violation: = range t.GetViolations() {
                    return [] string {
                        violation.GetType(),
                            violation.GetSubject(),
                            violation.GetSubject(),
                    }
                }
            default:
                return nil
        }
    }

    return nil
}

by using the code here - https://github.com/hyperium/tonic/blob/372da52e96114ca76cc221f3c598be82bfae970c/tests/integration_tests/tests/status.rs#L16-L20

i'm getting transport: malformed grpc-status-details-bin: proto: invalid field number on the Go side

bug

All 10 comments

Yeah, this is interesting, will see if I can re-produce this tomorrow. Thanks for the report!

@moh-abk how were you able to get that specific log?

@moh-abk how were you able to get that specific log?

Do you mean this error: Status { code: PreconditionFailure, message: "Invalid Argument", details: b"ClwKF2ludmFsaWRfcmVjaXBpZW50X3Bob25lEkFSZWNpcGllbnQgcGhvbmUgbnVtYmVyIGlzIG5vdCB2YWxpZCBmb3IgdGhlIGdpdmVuIG1vYmlsZSBvcGVyYXRvcg" }?

I mean this one transport: malformed grpc-status-details-bin: proto: invalid field number

I mean this one transport: malformed grpc-status-details-bin: proto: invalid field number

That came from Go on the other service attempting to access with_details

I doesn't work because in some implementations, Status is itself a protobuf. Errors are encoded as a repeated field of Any protos and the entire thing is sent to the client.

Something like this works with a go client. I used BadRequest but it should be the same for other errors.

async fn error_with_details(&self, _: Request<Empty>) -> Result<Response<Empty>, Status> {
    let error = crate::google_rpc::BadRequest {
        field_violations: vec![FieldViolation {
            field: "username".to_string(),
            description: "username is too short".to_string(),
        }],
    };

    let mut buffer = BytesMut::with_capacity(4096);
    error.encode(&mut buffer).expect("encoded bad request");

    let status_proto = crate::google_rpc::Status {
        code: Code::InvalidArgument as i32,
        message: "argument is invalid".to_string(),
        details: vec![prost_types::Any {
            type_url: "google.rpc.BadRequest".to_string(),
            value: buffer.to_vec(),
        }],
    };

    buffer.clear();
    status_proto.encode(&mut buffer).expect("encoded status");

    let status = Status::with_details(
        Code::InvalidArgument,
        "argument is invalid",
        buffer.freeze(),
    );

    Err(status)
}

This is the Status proto definition, which grpc-go uses internally:

https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto

I also created https://docs.rs/tonic-types/0.1.0/tonic_types/ which has the Status proto.

@alce where is crate::google_rpc::BadRequest and crate::google_rpc::Status coming from?

@moh-abk Those are just the compiled protos from https://github.com/googleapis/googleapis/tree/master/google/rpc

Closing this as it seems resolved

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rlabrecque picture rlabrecque  路  5Comments

xmclark picture xmclark  路  10Comments

cthulhua picture cthulhua  路  3Comments

ericmcbride picture ericmcbride  路  6Comments

LucioFranco picture LucioFranco  路  7Comments