From 5472bdfc0fc5dc163483f579654c8f9b0367dccc Mon Sep 17 00:00:00 2001 From: shuiyisong <113876041+shuiyisong@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:39:28 +0800 Subject: [PATCH] chore: return 404 if trace not found (#7304) * chore: return 404 if trace not found Signed-off-by: shuiyisong * chore: add test and fix Signed-off-by: shuiyisong --------- Signed-off-by: shuiyisong --- src/servers/src/http/jaeger.rs | 28 +++++++++++++++++++++++++++- tests-integration/tests/http.rs | 24 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/servers/src/http/jaeger.rs b/src/servers/src/http/jaeger.rs index 4c4ff8a1fc..148e2ac77a 100644 --- a/src/servers/src/http/jaeger.rs +++ b/src/servers/src/http/jaeger.rs @@ -56,6 +56,9 @@ pub const JAEGER_QUERY_TABLE_NAME_KEY: &str = "jaeger_query_table_name"; const REF_TYPE_CHILD_OF: &str = "CHILD_OF"; const SPAN_KIND_TIME_FMTS: [&str; 2] = ["%Y-%m-%d %H:%M:%S%.6f%z", "%Y-%m-%d %H:%M:%S%.9f%z"]; +const TRACE_NOT_FOUND_ERROR_CODE: i32 = 404; +const TRACE_NOT_FOUND_ERROR_MSG: &str = "trace not found"; + /// JaegerAPIResponse is the response of Jaeger HTTP API. /// The original version is `structuredResponse` which is defined in https://github.com/jaegertracing/jaeger/blob/main/cmd/query/app/http_handler.go. #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] @@ -67,6 +70,22 @@ pub struct JaegerAPIResponse { pub errors: Vec, } +impl JaegerAPIResponse { + pub fn trace_not_found() -> Self { + Self { + data: None, + total: 0, + limit: 0, + offset: 0, + errors: vec![JaegerAPIError { + code: TRACE_NOT_FOUND_ERROR_CODE, + msg: TRACE_NOT_FOUND_ERROR_MSG.to_string(), + trace_id: None, + }], + } + } +} + /// JaegerData is the query result of Jaeger HTTP API. #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(untagged)] @@ -474,6 +493,10 @@ pub async fn handle_get_trace( match covert_to_records(output).await { Ok(Some(records)) => match traces_from_records(records) { + Ok(traces) if traces.is_empty() => ( + HttpStatusCode::NOT_FOUND, + axum::Json(JaegerAPIResponse::trace_not_found()), + ), Ok(traces) => ( HttpStatusCode::OK, axum::Json(JaegerAPIResponse { @@ -486,7 +509,10 @@ pub async fn handle_get_trace( error_response(err) } }, - Ok(None) => (HttpStatusCode::OK, axum::Json(JaegerAPIResponse::default())), + Ok(None) => ( + HttpStatusCode::NOT_FOUND, + axum::Json(JaegerAPIResponse::trace_not_found()), + ), Err(err) => { error!("Failed to get trace '{}': {:?}", trace_id, err); error_response(err) diff --git a/tests-integration/tests/http.rs b/tests-integration/tests/http.rs index 0ee830c505..f5c1542ef8 100644 --- a/tests-integration/tests/http.rs +++ b/tests-integration/tests/http.rs @@ -6575,6 +6575,30 @@ pub async fn test_jaeger_query_api_for_trace_v1(store_type: StorageType) { let expected: Value = serde_json::from_str(expected).unwrap(); assert_eq!(resp, expected); + // Test `/api/traces/{trace_id}` API for non-existent trace. + let res = client + .get("/v1/jaeger/api/traces/0000000000000000000000000000dead") + .header("x-greptime-trace-table-name", trace_table_name) + .send() + .await; + assert_eq!(StatusCode::NOT_FOUND, res.status()); + let expected = r#"{ + "data": null, + "total": 0, + "limit": 0, + "offset": 0, + "errors": [ + { + "code": 404, + "msg": "trace not found" + } + ] +} +"#; + let resp: Value = serde_json::from_str(&res.text().await).unwrap(); + let expected: Value = serde_json::from_str(expected).unwrap(); + assert_eq!(resp, expected); + // Test `/api/traces` API. let res = client .get("/v1/jaeger/api/traces?service=test-jaeger-query-api&operation=access-mysql&start=1738726754492421&end=1738726754642422&tags=%7B%22operation.type%22%3A%22access-mysql%22%7D")