Skip to content

fix(observability): classify OpenHuman/Embedding backend 'Invalid token' 401 as SessionExpired (TAURI-RUST-4P0 + 4K5)#2786

Open
CodeGhost21 wants to merge 2 commits into
tinyhumansai:mainfrom
CodeGhost21:fix/observability-openhuman-invalid-token
Open

fix(observability): classify OpenHuman/Embedding backend 'Invalid token' 401 as SessionExpired (TAURI-RUST-4P0 + 4K5)#2786
CodeGhost21 wants to merge 2 commits into
tinyhumansai:mainfrom
CodeGhost21:fix/observability-openhuman-invalid-token

Conversation

@CodeGhost21
Copy link
Copy Markdown
Contributor

@CodeGhost21 CodeGhost21 commented May 27, 2026

Summary

Extend is_session_expired_message in src/core/observability.rs to recognise the OpenHuman backend's {"success":false,"error":"Invalid token"} 401 envelope as a session-expired condition (currently only the explicit "Session expired. Please log in again." body is recognised). Two emit-site wrapper shapes both route through the same report_error_or_expected funnel and need to be covered:

Sentry ID Emit-site prefix Wrapped by
TAURI-RUST-4P0 OpenHuman API error (401 …) chat / agent / web-channel run_chat_task
TAURI-RUST-4K5 Embedding API error (401 …) src/openhuman/embeddings/openai.rs:139

Both surface the same upstream cause — backend rejecting the bearer JWT as invalid — typically right after a [scheduler_gate] signed_out false -> true breadcrumb (visible in the 4K5 event payload). UI already drives reauth via the SessionExpired event-domain path; this stops the noise leaking into Sentry as a code bug.

Why two arms, not one

The matcher uses conjunctive anchors per arm — "<emit-site prefix> (401" AND the envelope-shaped "\"error\":\"Invalid token\"". Anchoring on the emit-site prefix is what preserves the existing #2286 BYO-key contract:

  • A third-party "OpenAI API error (401 Unauthorized): invalid_api_key" (user's own OpenAI key revoked) must NOT match — it's actionable misconfiguration.
  • A third-party "Embedding API error (401 Unauthorized): invalid_api_key" (user's own embedding-provider key revoked) must NOT match for the same reason — covered by the new does_not_classify_embedding_byo_key_401_as_session_expired polarity guard.

A single broad "any 401 + invalid token" matcher would silence both — that's why each emit-site wrapper gets its own prefix-gated arm.

Tests added

  • classifies_openhuman_invalid_token_401_as_session_expired — verbatim 4P0 wire shape (wrapped + unwrapped variants).
  • classifies_embedding_api_invalid_token_401_as_session_expired — verbatim 4K5 wire shape (direct + wrapped variants).
  • does_not_classify_embedding_byo_key_401_as_session_expired — polarity guard pinning the BYO-key contract for the embedding path.
  • Existing does_not_classify_byo_key_provider_401_as_session_expired continues to pass — SessionExpired clears the session for unrelated backend 401s #2286 contract for the chat path preserved.

Test plan

  • cargo test classifies_embedding_api_invalid_token — passes (new)
  • cargo test classifies_openhuman_invalid_token — passes (4P0)
  • cargo test does_not_classify_embedding_byo_key — passes (new polarity guard)
  • cargo test does_not_classify_byo_key_provider — passes (SessionExpired clears the session for unrelated backend 401s #2286 contract)
  • cargo test core::observability — 91 tests pass, 0 regressions
  • cargo check --bin openhuman-core — passes
  • cargo fmt --check — clean

Related

  • Sentry-Issue: TAURI-RUST-4P0
  • Sentry-Issue: TAURI-RUST-4K5
  • Sentry-Issue: TAURI-RUST-4QP — same root cause as 4K5 (Embedding API 401 "Invalid token" envelope), different Sentry fingerprint; the conjunctive-anchor matcher in this PR demotes both shortIds.

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection and classification of authentication "session expired" errors to recognize additional provider-specific 401 error formats while avoiding false positives for other 401 variants.
    • Expanded tests to cover the newly recognized error formats and to ensure other similar 401 responses remain correctly classified.

Review Change Stack

… SessionExpired

The OpenHuman backend rejects an expired/revoked JWT with the envelope
`{"success":false,"error":"Invalid token"}` (vs. the explicit
`"Session expired. Please log in again."` body that the existing
classifier already catches). Same emit site
(`providers::ops::api_error` → `web_channel.run_chat_task`), same
wrapping, same expected user state — just a different body substring
chosen by the backend's JWT-validity branch.

Issue tinyhumansai#2286 deliberately stopped matching bare `"Invalid token"` as
session-expired because that string also surfaces from Discord /
OAuth provider rejections, which are actionable scoped errors that
must reach Sentry. We preserve that contract with a conjunctive
matcher: BOTH the OpenHuman-scoped `"OpenHuman API error (401"`
prefix AND the envelope-shaped `"\"error\":\"Invalid token\""` must
be present.

tinyhumansai#2286 cases still route to Sentry (verified by the existing
`does_not_classify_byo_key_provider_401_as_session_expired` test
staying green):

- `"Invalid token"`                                   → None ✓
- `"got an invalid token here"`                       → None ✓
- `"OpenAI API error (401 Unauthorized): invalid_api_key"`  → None ✓
- `"Anthropic API error (401 Unauthorized): ..."`     → None ✓

Targets Sentry OPENHUMAN-TAURI-4P0 (issue 5332): low volume so far
(1 event) but the wire shape is durable — every OpenHuman user with
a stale JWT will hit this on the next agent turn, so quietly
demoting it to a `warn!` log keeps the noise from compounding.
@CodeGhost21 CodeGhost21 requested a review from a team May 27, 2026 20:16
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

Adds conjunctive matching and docs in is_session_expired_message to classify OpenHuman/Embedding 401 "Invalid token" JSON envelopes as SessionExpired, and adds regression tests covering wrapped/unwrapped and negative BYO-key cases.

Changes

OpenHuman 401 Invalid Token Classification

Layer / File(s) Summary
Session-expired classification for OpenHuman 401 invalid-token
src/core/observability.rs
Doc comment expanded to describe the OpenHuman 401 {"error":"Invalid token"} envelope and the embedding wrapper; is_session_expired_message updated to conjunctively require the "OpenHuman API error (401" or "Embedding API error (401" prefix plus the exact invalid-token envelope before returning ExpectedErrorKind::SessionExpired.
Regression tests for invalid-token classification
src/core/observability.rs
New tests assert both wrapped run_chat_task and unwrapped OpenHuman invalid-token envelopes classify as SessionExpired; verify embedding invalid-token wrapper classifies as SessionExpired; and guard that BYO-key/generic 401 variants do not classify as session-expired.

Sequence Diagram(s)

(omitted — change is a targeted classifier update and tests; no multi-component sequential flow requires visualization)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • senamakel
  • graycyrus
  • M3gA-Mind

Poem

🐰 A token slipped out of date and key,
The 401 whispered, "Invalid token" to me.
I matched prefix and envelope with care,
So sessions expire only when they truly bear.
Hooray — tests hop in, the classifier set free!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The PR title accurately describes the main change: classifying OpenHuman/Embedding backend 'Invalid token' 401 responses as SessionExpired for proper error handling.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@coderabbitai coderabbitai Bot added working A PR that is being worked on by the team. bug labels May 27, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CodeGhost21 hey! the code looks good to me — the conjunctive anchor approach is exactly right here. Requiring both the "OpenHuman API error (401" prefix and the envelope-shaped "\"error\":\"Invalid token\"" body together is the correct way to preserve the #2286 contract while catching this specific backend branch, and the two new test cases pin both the verbatim Sentry wire shape and the unwrapped emit shape cleanly.

CI still has a few checks pending (Windows E2E, core image build, Rust core coverage). Once those come back green I'll come back and approve this. Let me know if anything comes up in the meantime.

…sionExpired (TAURI-RUST-4K5)

TAURI-RUST-4K5 (~118 events, escalating on 0.56.0,
domain=embeddings operation=openai_embed status=401) carries the same
OpenHuman backend `{"success":false,"error":"Invalid token"}` envelope
as 4P0, but the embedding client at
`src/openhuman/embeddings/openai.rs:139` wraps it with the
`"Embedding API error"` prefix instead of `"OpenHuman API error"`. The
breadcrumb `[scheduler_gate] signed_out false -> true` immediately
preceding the 401 in the event payload confirms it's the same
session-expired cause, just emitted at the embedding layer.

The conjunctive `"OpenHuman API error (401"` anchor added in the
previous commit catches the chat-API path; this commit adds a parallel
`"Embedding API error (401"` anchor so the embedding path also routes
to SessionExpired. The envelope-shaped `"\"error\":\"Invalid token\""`
gate stays the same, so third-party BYO-key embedding 401s (OpenAI /
Voyage / Cohere rejecting the user's own API key) continue to escalate
as actionable misconfiguration — covered by the new
`does_not_classify_embedding_byo_key_401_as_session_expired`
polarity guard.

## Test plan
- [x] `cargo test classifies_embedding_api_invalid_token` — passes (new)
- [x] `cargo test does_not_classify_embedding_byo_key` — passes (new polarity guard)
- [x] `cargo test classifies_openhuman_invalid_token` — passes (4P0, unchanged)
- [x] `cargo test does_not_classify_byo_key_provider` — passes (tinyhumansai#2286 BYO-key contract preserved)
- [x] `cargo test core::observability` — 91 tests pass, 0 regressions
- [x] `cargo check --bin openhuman-core` — passes
- [x] `cargo fmt --check` — clean
@CodeGhost21 CodeGhost21 changed the title fix(observability): classify OpenHuman backend 'Invalid token' 401 as SessionExpired fix(observability): classify OpenHuman/Embedding backend 'Invalid token' 401 as SessionExpired (TAURI-RUST-4P0 + 4K5) May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants