Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,11 @@ pub fn run() {
);
return None;
}
if openhuman_core::core::observability::is_transient_backend_api_failure(&event)
|| openhuman_core::core::observability::is_transient_integrations_failure(&event)
{
return None;
}
// Strip server_name (hostname) to avoid leaking machine identity.
event.server_name = None;
event.user = None;
Expand Down
54 changes: 42 additions & 12 deletions src/api/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,42 @@ impl BackendOAuthClient {
}

let response = request.send().await.map_err(|e| {
crate::core::observability::report_error(
e.to_string().as_str(),
"backend_api",
"authed_json",
&[
("method", method.as_str()),
("path", url.path()),
("failure", "transport"),
],
);
// Walk the error source chain so transient markers hidden in nested
// causes (reqwest -> hyper -> rustls TLS EOF, etc.) still classify
// correctly. The top-level `e.to_string()` often only carries the
// outermost wrapper, e.g. "error sending request for url (...)".
let mut error_message = e.to_string();
let mut src: Option<&(dyn std::error::Error + 'static)> = std::error::Error::source(&e);
while let Some(s) = src {
error_message.push_str(" → ");
error_message.push_str(&s.to_string());
src = s.source();
}
if crate::core::observability::contains_transient_transport_phrase(&error_message) {
tracing::warn!(
domain = "backend_api",
operation = "authed_json",
method = method.as_str(),
path = url.path(),
failure = "transport",
error = %error_message,
"[backend_api] transient transport failure on {} {}: {}",
method.as_str(),
url.path(),
error_message,
);
} else {
crate::core::observability::report_error(
error_message.as_str(),
"backend_api",
"authed_json",
&[
("method", method.as_str()),
("path", url.path()),
("failure", "transport"),
],
);
}
anyhow::Error::new(e).context(format!(
"backend request {} {}",
method.as_str(),
Expand All @@ -445,15 +471,19 @@ impl BackendOAuthClient {
if !status.is_success() {
let status_code = status.as_u16();
let status_str = status_code.to_string();
// 502/503/504 are transient infrastructure errors (proxy/CDN/backend
// These are transient infrastructure errors (proxy/CDN/backend
// temporarily unavailable). They are not code bugs and callers already
// implement retry/disable logic, so skip Sentry to avoid noise.
let is_transient_infra = matches!(status_code, 502 | 503 | 504);
let is_transient_infra =
crate::core::observability::is_transient_http_status_code(status_code);
if is_transient_infra {
tracing::warn!(
domain = "backend_api",
operation = "authed_json",
method = method.as_str(),
path = url.path(),
status = status_code,
failure = "non_2xx",
"[backend_api] transient {status} on {} {} — not reporting to Sentry",
method.as_str(),
url.path(),
Expand Down
20 changes: 20 additions & 0 deletions src/core/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,26 @@ pub async fn rpc_handler(State(state): State<AppState>, Json(req): Json<RpcReque
);
} else if is_session_expired_error(&display_message) {
tracing::info!("[rpc] {} -> err ({}ms): {}", method, ms, display_message);
} else if crate::core::observability::is_transient_message_failure(&display_message) {
// Downstream call (backend_api / integrations / provider) already
// demoted the underlying transient failure to a warn. The error
// string still propagates up to here; re-reporting at error level
// would re-create the very Sentry noise the lower-layer demote
// was meant to avoid (#8Z, #93, #8W, #96).
//
// Redact before logging — `display_message` is upstream-derived
// (backend / provider response) and can carry URL fragments,
// query params, or pasted-through provider error text that
// includes tokens. `sanitize_api_error` runs the same scrub
// used in the SessionExpired publish path below.
let redacted =
crate::openhuman::providers::ops::sanitize_api_error(&display_message);
tracing::warn!(
method = %method,
elapsed_ms = ms as u64,
error = %redacted,
"[rpc] transient downstream failure — not reporting to Sentry (message redacted)"
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else {
crate::core::observability::report_error_or_expected(
display_message.as_str(),
Expand Down
Loading
Loading