grpc / grpc/grpc-rust

Status code replaced with `Code::Internal` on client when returning `Err(Status)` with `Code::Ok` on server

Open
#2,074 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
12.5k
Forks
1.3k
Avg merge
4d 7h
Merged PRs (30d)
24

Description

## A big question

most likely it's not a bug with tonic or grpc, but i'd like to clarify if things work as expected before making conclusions.

### Versions

first noticed it with these older versions cos we didn't update for a while

```toml
[dependencies]
tonic = "0.11"
prost = "0.12"
prost-types = "0.12"

[build-dependencies]
tonic-build = "0.11"
prost-build = "0.12"
```

but it behaves exactly the same in the latest version

```toml
[dependencies]
tonic = "0.12.3"
prost = "0.13.3"
prost-types = "0.13.3"

[build-dependencies]
tonic-build = "0.12.3"
prost-build = "0.13.3"
```

### Platform

seems irrelevant, but still

- 23.6.0 Darwin Kernel Version 23.6.0 arm64

also occurs when communicating between my macos client and other linux server (unfortunately, i don't know distro is there)

### Description

#### How we came to this question

imagine a legacy system that monitors some state in real time.
it also has this grpc api described in `demo.proto` - see a minimal example below.
unfortunately, we can't modify the `.proto` in the nearest future because it will break compatibility with other systems.

```protobuf
syntax = "proto3";

package demo;

service DemoService {
rpc GetStatus(Empty) returns (SystemStatus);
}

message Empty {}

message SystemStatus {
string some_crazy_state = 1;
}
```

fields in `SystemStatus` are irrelevant for this example.

all you need to know is that, after many big updates, the server that implements this protocol can no longer represent its state in a meaningful way via the old `SystemStatus`. (on the server, it was just a struct, but now it's a more complex enum).

as a silly workaround, just to let the client know that the system is in a special state,
we made one of the branches that returns the status look kinda like this (stripped down to the basics again)

```rust
mode demo {
tonic::include_proto!("demo");
}

struct MyService;

#[tonic::async_trait]
impl demo::demo_service_server::DemoService for MyService {
async fn get_status(
&self,
_request: tonic::Request,
) -> tonic::Result> {
Err(tonic::Status::new(
tonic::Code::Ok,
"Cannot return meaningful response, but the service works fine",
))
}
}
```

so as the code suggests, the `tonis::Status` with `Code::Ok` and some text message is intended to tell the client that the server is not dead and is working properly, but just cannot give a proper expected response at this moment (will be able to in a few seconds).

#### Actual behavior on the client

the following call on the client

```rust
let result = client.get_status(demo::Empty{}).await;
dbg!(result);
```

prints the following

```txt
result = Err(
Status {
code: Internal,
message: "Missing response message.",
source: None,
},
)
```

which is different from what was coded on the server.

#### Expected behavior?

here is what makes me confused.

first, `tonic` does not prevent me from doing things this way, therefore, on the first glance, it looks like a valid solution, simply because nobody told me "you can't do that". since rpc "looks like a regular function call" then to me it seems like that the client should get exactly what was sent from the server. and if we draw parallels with http, nobody prevents you from returning 404 not found instead of 204 no content - it's not recommended, but it's possible :trollface:

second, postman adds fuel to the fire, because it simply shows what i'd expect. that's how we tested the server at first, and that's why we didn't realize at first that our `tonic` client implementation works differently

image

and finally, we went through some md files in grpc repo, but couldn't find a concrete answer to how exactly it should behave.

### The real question

to avoid confusion now and in the future, i would like to ask the developers to clarify - what's the intended behavior when we return `tonic::Status` with `Code::Ok` and arbitrary text message in the `Result::Err` branch from the server?

(although i admit this edge case is quite dumb, and there are many other, probably more fitting status codes to use)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.