grpc / grpc/grpc-rust

Received Status.source = None when server sends Status.source = Some

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

Description

## Bug Report

### Version
tonic v0.11.0
tonic-build v0.11.0

### Platform
Linux 6.5.0-21-generic #21~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 9 13:32:52 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

### Description
#### Received Status.source field is None even though it is Some when sent from the server.

Situation:
GRPC server built with tonic-build encounters an error `err` and returns a `tonic::Status` with `source: err`, the client, (also built with tonic-build) receives the Status but the `source` is `None`.

I expected the `source` field of the received Status on the client's side to be populated with the same information that was sent from the client.

Below is a demo server and client cargo project as a zip file, the `server.rs` and `client.rs` files are also below for reference, along with their output.
The server simply returns a Status where the source of the status has been set.

To run the demo to reproduce, extract the zip, from within the zip;
- run `cargo run --bin server` in one terminal.
- run `cargo run --bin client` in another terminal.
See that the log message from the client states `received error: Status {..., source: None }` however the log in the server shows `sending error: Status {..., source: Some(Custom ...)}`

[tonic_status_source_bug_demo.zip](https://github.com/hyperium/tonic/files/14576264/tonic_status_source_bug_demo.zip)

#### server.rs
```rust
use std::sync::Arc;

use tonic::{transport::Server, Request, Response, Status};

pub mod error_server {
tonic::include_proto!("error_server");
}

#[derive(Debug, Default)]
pub struct ErrorServerService {}

#[tonic::async_trait]
impl error_server::error_server_server::ErrorServer for ErrorServerService {
/// Just return error
async fn error_server(
&self,
_: Request,
) -> Result, Status> {
let source_error = std::io::Error::new(std::io::ErrorKind::Other, "oh no!");
let mut status = tonic::Status::internal("error:");
status.set_source(Arc::new(source_error));
log::error!("sending error: {:?}", status);
return Err(status);
}
}

#[tokio::main]
async fn main() -> Result<(), Box> {
let env = env_logger::Env::default()
.filter_or("LOG_LEVEL", "info")
.write_style_or("LOG_STYLE", "never");
env_logger::init_from_env(env);

let port = std::env::var("PORT").unwrap_or_else(|_| "50051".to_string());
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port.parse()?));
let error_server = ErrorServerService::default();
log::info!("serving on {addr}");

Server::builder()
.add_service(error_server::error_server_server::ErrorServerServer::new(
error_server,
))
.serve(addr)
.await?;

Ok(())
}

```

#### client.rs
```rust
use tonic::transport::Channel;

pub mod error_server {
tonic::include_proto!("error_server");
}

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let env = env_logger::Env::default()
.filter_or("LOG_LEVEL", "info")
.write_style_or("LOG_STYLE", "never");
env_logger::init_from_env(env);

let port = std::env::var("PORT").unwrap_or_else(|_| "50051".to_string());
let endpoint = format!("https://localhost:{port}");
// log::info!("sending requests to {host}:{port}");
let channel = Channel::from_shared(endpoint)?.connect().await?;
log::info!("channel connected");

let mut client = error_server::error_server_client::ErrorServerClient::new(channel);

let request = tonic::Request::new(error_server::Request {
text: "Hello World".to_string(),
});

match client.error_server(request).await {
Err(status) => {
// debug print the error showing that source is None.
log::error!("received error: {:?}", status);
Err(status.into())
}
Ok(response) => {
log::info!("received response: {:?}", response.into_inner());
Ok(())
}
}
}

```

#### Server output:
```
[2024-03-12T16:03:28Z INFO server] serving on 0.0.0.0:50052
[2024-03-12T16:04:20Z ERROR server] sending error: Status { code: Internal, message: "error:", source: Some(Custom { kind: Other, error: "oh no!" }) }
```

#### Client output:
```
[2024-03-12T16:04:20Z ERROR client] received error: Status { code: Internal, message: "error:", metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Tue, 12 Mar 2024 16:04:20 GMT", "content-length": "0"} }, source: None }
Error: status: Internal, message: "error:", details: [], metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Tue, 12 Mar 2024 16:04:20 GMT", "content-length": "0"} }
```

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.