fix(observability): classify SessionExpired at agent layer (OPENHUMAN-TAURI-26)#1763
Conversation
…-TAURI-26)
`agent.run_single` and `web_channel.run_chat_task` call `report_error_or_expected`
*before* the JSON-RPC dispatch boundary sees the error string, so the
session-expired classifier living privately in `core::jsonrpc.rs` never ran
against them. Mid-conversation 401s emitted by the OpenHuman backend
("OpenHuman API error (401 Unauthorized): {…Session expired…}") therefore
escaped as Sentry error events from the agent layer — 15 events on
OPENHUMAN-TAURI-26, all from release 0.53.22 before tinyhumansai#1516's cascade
dampener reduced volume but did not close this code path.
Adds `ExpectedErrorKind::SessionExpired` + a strict shared classifier
`is_session_expired_message` in `core::observability`, anchored on
canonical OpenHuman session phrasing ("session expired", "no backend
session token", "session JWT required", and the `SESSION_EXPIRED`
sentinel) so the demote does not silence BYO-key OpenAI / Anthropic 401s
(those are actionable misconfigurations and must reach Sentry).
The dispatch-site classifier (`core::jsonrpc::is_session_expired_error`)
keeps its existing **looser** "401 + unauthorized" / "invalid token"
match — token cleanup and `DomainEvent::SessionExpired` publish still
fire on every 401 propagated to `invoke_method`, so the auto-cleanup
teardown is unchanged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR adds a new ChangesSession-expired error classification
Possibly related PRs
Suggested labels
Suggested reviewers
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
…ore_send wire Recreates PR tinyhumansai#1719's surface against the current upstream/main (the prior branch was 20+ commits behind and `SessionExpired` enum + matcher `is_session_expired_message` + `expected_error_kind` arm + per-kind `report_expected_message` arm all landed via tinyhumansai#1763, so the original classifier-extension commits were duplicates). Three remaining unique deltas land here in one commit: - `is_session_expired_event(event)` in `src/core/observability.rs` — tag-and-body classifier that drops `(domain=llm_provider|backend_api|rpc) + status=401 + body matches is_session_expired_message` shapes, plus the pre-flight rpc dispatcher path that has no status tag. Composio's OAuth-state 401 is intentionally excluded — that's actionable and must reach Sentry. - Bilateral before_send wire in `src/main.rs` (core binary) and `app/src-tauri/src/lib.rs` (Tauri shell — since tinyhumansai#1061 it links the core in-process, so any session-expired event captured by either surface lands in the same Sentry client and must be filtered identically). Both filters log only `event.event_id` per CR feedback from PR tinyhumansai#1719 — `event.message` carries the raw backend response body which CLAUDE.md forbids from local logs. - Smoke test runtime path in `tests/observability_smoke.rs` — imports the new classifier and threads it into the same `before_send` chain shape the real binary installs, so the runtime drop is covered end- to-end alongside the existing budget / transient / updater filters. Drops OPENHUMAN-TAURI-25 / -1Q / -27 / -1G (~185 events/day combined). The original PR's emit-site demotions (compatible.rs / authed_json / agent run_single) were superseded by tinyhumansai#1763 — leaving them out keeps this PR scoped to the before_send defense-in-depth that tinyhumansai#1763 didn't ship. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
agent.run_singleandweb_channel.run_chat_taskcallreport_error_or_expectedbefore the JSON-RPC dispatch boundary's session-expired filter ever runs, so mid-conversation 401s from the OpenHuman backend ("OpenHuman API error (401 Unauthorized): {…Session expired…}") escaped as Sentry error events from the agent layer — OPENHUMAN-TAURI-26 (15 events on release 0.53.22 before fix(auth): stop 401 cascade after session expiry (OPENHUMAN-TAURI-1T) #1516's cascade dampener reduced volume but did not close the code path).ExpectedErrorKind::SessionExpiredand a strict shared classifieris_session_expired_messageincore::observability. Anchored on canonical OpenHuman session phrasing ("session expired","no backend session token","session JWT required",SESSION_EXPIREDsentinel) so the demote does not silence BYO-key OpenAI / Anthropic 401s — those are actionable misconfigurations and must keep reaching Sentry.core::jsonrpc::is_session_expired_error) stays looser on"401 + unauthorized"/"invalid token"so token cleanup andDomainEvent::SessionExpiredpublish still fire on every 401 propagated toinvoke_method. Auto-cleanup teardown unchanged.Why two classifiers, not one
The dispatch-site predicate needs to be broad enough to clear the local token defensively on any 401 (including a misconfigured BYO-key — clearing stale state is safe). The agent-layer demote must be narrow so a BYO-key 401 still reaches Sentry as an actionable error. Same boundary, different jobs — the docstrings on both helpers call out the relationship explicitly.
Test plan
cargo test --lib observability— 45 pass, new tests cover OPENHUMAN-TAURI-26 wire shape (with caller wrapping), theSESSION_EXPIREDsentinel, "no backend session token" / "session JWT required" pre-flight guards, and explicitly assert BYO-key provider 401s (OpenAI / Anthropic) are not silenced.cargo test --lib jsonrpc— 57 pass, existingis_session_expired_error_*suite still green (the looser dispatch-site classifier is unchanged in behavior).cargo fmt+cargo checkon core (Cargo.toml) and Tauri shell (app/src-tauri/Cargo.toml).Closes
Fixes OPENHUMAN-TAURI-26
Summary by CodeRabbit