Skip to content

fix(team): preserve anyhow cause chain in backend RPC error envelopes (OPENHUMAN-TAURI-AD)#1647

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
CodeGhost21:fix/team-ops-preserve-cause-chain-ad
May 13, 2026
Merged

fix(team): preserve anyhow cause chain in backend RPC error envelopes (OPENHUMAN-TAURI-AD)#1647
senamakel merged 1 commit into
tinyhumansai:mainfrom
CodeGhost21:fix/team-ops-preserve-cause-chain-ad

Conversation

@CodeGhost21
Copy link
Copy Markdown
Contributor

@CodeGhost21 CodeGhost21 commented May 13, 2026

Summary

This is not a Sentry-skip — it's the inverse. The current Sentry events are too vague to act on; the fix makes them actionable so we can diagnose the actual root cause.

Fixes OPENHUMAN-TAURI-AD (2 occurrences on openhuman@0.53.35, Windows user in St. Petersburg, Russia).

Why the chain matters here

api::rest::authed_json wraps the underlying reqwest error with .context(format!(\"backend request {} {}\", method, path)). The anyhow chain at the failure site is:

  • outer: \"backend request GET /teams\"
  • cause: reqwest::Error — could be connect timeout, DNS failure, TLS handshake error, immediate connection refused, non-2xx HTTP status, …

e.to_string() renders only the outer label. The Sentry events all read \"backend request GET /teams\" with no underlying cause — and elapsed_ms=49 is way too short for a real network timeout, so the true cause (immediate refusal? token rejected mid-request? URL parse error?) is the only signal worth surfacing.

{e:#} (anyhow's alternate format) joins the outer context plus every wrapped cause with \": \", so the next event will tell us why the request failed and we can fix the underlying condition.

Why fix once in get_authed_value

Every team RPC (list_teams, get_team, list_members, get_usage, create_team, update_team, delete_team, switch_team, leave_team, join_team) routes through this one helper, so a single change covers the whole domain — no per-handler fanout needed.

Test plan

  • cargo check --lib — no new warnings.
  • cargo fmt.
  • No new tests: the {e:#} rendering contract is already covered by core::observability::tests::anyhow_chain_is_rendered_in_full. Behavioural coverage here would require a synthetic reqwest failure fixture — disproportionate for a single-helper format-specifier fix.

Follow-up

Once this ships and the next Sentry event lands with the cause chain visible, a targeted follow-up PR can address the underlying condition. The 49ms elapsed time argues against "network unreachable" (PR #1601 would silence those once their full message includes \"error sending request for url\" via this chain) — more likely an immediate transport refusal or auth boundary, both of which warrant their own handling.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error message formatting to display complete error context chains, providing more detailed information for diagnosing issues.
  • Documentation

    • Added inline comments documenting how error messages are surfaced in the system.

Review Change Stack

… (OPENHUMAN-TAURI-AD)

`team::ops::get_authed_value` renders the inbound `anyhow::Error` with
plain `e.to_string()`, which only emits the outermost context and drops
every wrapped cause. The HTTP layer wraps the underlying reqwest error
with `.context(format!("backend request {} {}", …))` in
`api/rest.rs::authed_json`, so the chain at the failure site looks like:

  outer: "backend request GET /teams"
  cause: <reqwest::Error — connect timeout, DNS failure, TLS handshake,
         non-2xx status response, …>

`e.to_string()` renders only the outer label, so Sentry events for
OPENHUMAN-TAURI-AD all read `backend request GET /teams` with no
underlying cause — undiagnosable. The reported event has
`elapsed_ms=49`, which is far too short for a real network timeout: the
true cause (immediate connection refused? token rejected mid-request?
URL parse error?) is the only signal worth surfacing, and it is
exactly what the chain carries.

This is the same failure mode the `report_error` doc-string in
`core/observability.rs` calls out (referencing OPENHUMAN-TAURI-B2) —
and the same one anyhow's alternate format specifier `{e:#}` is
designed for: outer context + every wrapped cause, joined by ": ".

Switch both `map_err` sites in `get_authed_value` to `{e:#}`. Because
every team RPC (`list_teams`, `get_team`, `list_members`, `get_usage`,
`create_team`, `update_team`, `delete_team`, `switch_team`,
`leave_team`, `join_team`) routes through this one helper, the single
fix covers the whole domain.

No new tests: the `{e:#}` rendering contract is already covered by
`core::observability::tests::anyhow_chain_is_rendered_in_full`, and
behavioural coverage here would require a synthetic reqwest failure
fixture — disproportionate for a single-helper format-specifier fix.
@CodeGhost21 CodeGhost21 requested a review from a team May 13, 2026 14:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3b350321-9df8-4ff1-b123-389b9fdb8349

📥 Commits

Reviewing files that changed from the base of the PR and between 9160317 and 80d9eb2.

📒 Files selected for processing (1)
  • src/openhuman/team/ops.rs

📝 Walkthrough

Walkthrough

get_authed_value now formats backend client construction and request failures using Rust's {e:#} rendering to preserve full error chain context instead of simple to_string() display. Inline comments were added to document the improved error diagnostic visibility.

Changes

Backend error diagnostics

Layer / File(s) Summary
Backend error formatting with chained context
src/openhuman/team/ops.rs
get_authed_value switches backend client construction and authed request failures to use format!("{e:#}"), preserving the full error chain for better diagnostics, with comments explaining the change.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • senamakel

Poem

🐰 Errors once whispered their surface tales,
Now chains of context rise through the veils;
With {e:#} format, diagnostics sing,
Full cause and context—a debugging wing!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: preserving the anyhow cause chain in error rendering for team RPC error handling, which matches the core technical change in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@senamakel senamakel merged commit 39f641d into tinyhumansai:main May 13, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants