Received Status.source = None when server sends Status.source = Some
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 serverin one terminal. - run
cargo run --bin clientin another terminal. See that the log message from the client statesreceived error: Status {..., source: None }however the log in the server showssending error: Status {..., source: Some(Custom ...)}
tonic_status_source_bug_demo.zip
server.rs
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<error_server::Request>,
) -> Result<Response<error_server::Response>, 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<dyn std::error::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 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
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"} }