Skip to content

fix(observability,composio): demote direct-mode Composio 401/Invalid API key noise (Sentry TAURI-RUST-X9)#2481

Open
oxoxDev wants to merge 3 commits into
tinyhumansai:mainfrom
oxoxDev:fix/sentry-1166-composio-direct-auth-symmetry
Open

fix(observability,composio): demote direct-mode Composio 401/Invalid API key noise (Sentry TAURI-RUST-X9)#2481
oxoxDev wants to merge 3 commits into
tinyhumansai:mainfrom
oxoxDev:fix/sentry-1166-composio-direct-auth-symmetry

Conversation

@oxoxDev
Copy link
Copy Markdown
Contributor

@oxoxDev oxoxDev commented May 22, 2026

Summary

  • Restore symmetric error routing on Composio direct-mode call sites that were silently bypassing the classifier ladder.
  • Add an expected_error_kind arm for the canonical [composio-direct] … HTTP 401 / Invalid API key shape so user-supplied bad API keys land at debug instead of Sentry.
  • Closes the leak behind Sentry TAURI-RUST-X9 (15,732 events in ~22h from a single user on openhuman@0.54.0).
  • Adds 9 unit tests pinning the new classifier + the failure-tag / status-extraction contract on the canonical body.

Problem

Composio direct mode (shipped via #1825, 2026-05-15) added per-tenant list_connections / authorize / list_tools call sites on the agent-integrations path, but the direct branches did not wire their error closures through report_composio_op_error() the way the backend branches do. The classifier ladder in src/core/observability.rs::expected_error_kind was therefore never consulted for direct-mode errors, so an invalid user API key (ak_…) returned a raw 401 to the JSON-RPC layer where it captured to Sentry on every 5 s UI poll and every server-side composio::periodic tick.

Result: Sentry TAURI-RUST-X9 accumulated 15,732 events in ~22 hours from a single affected user (release openhuman@0.54.0+c25fc8e5fd3e). Bug-shape was user-config (their own key was bad), not a runtime fault — it is exactly the kind of shape the existing classifier ladder is designed to demote.

Two compounding gaps:

  1. Asymmetric reporting — direct-mode .map_err closures never called report_composio_op_error even though the backend branch in the same handler did. The classifier never had a chance to see the message.
  2. Missing matcher — even after fixing the asymmetry, expected_error_kind had no arm for the [composio-direct] … HTTP 401 / Invalid API key shape, so the message would still have classified as a real error → Sentry capture.

Solution

Three micro-commits, each independently revertible.

Commit 1 — fix(composio): restore symmetric reporting

Add report_composio_op_error(op, &e) to every direct-mode .map_err closure that was missing it, mirroring the backend branch pattern exactly:

  • src/openhuman/composio/ops.rscomposio_list_connections (direct branch), composio_authorize (direct branch), composio_list_tools prefetch + main call (direct branches). Widened report_composio_op_error from private to pub(super) so siblings (tools, periodic) can call it.
  • src/openhuman/composio/tools.rsComposioListConnectionsTool::execute direct branch (the only tool-side direct branch that issues a network call against the user's tenant).
  • src/openhuman/composio/periodic.rsrun_one_tick direct branch.

composio_delete_connection already routes through the unified resolve_client path, so the single .map_err at ops.rs:362 covers both modes; no change needed.

Commit 2 — fix(observability): new classifier arm

Add a matcher in is_provider_user_state_message (src/core/observability.rs) that recognizes the [composio-direct] caller prefix combined with one of HTTP 401 / Invalid API key. Routes to the existing ExpectedErrorKind::ProviderUserState variant (no new enum variants), which downgrades to debug log and skips Sentry.

The combined prefix anchor ([composio-direct] is unique to our own callers in src/openhuman/composio/*) keeps the matcher discriminating — unrelated 401s elsewhere in the codebase are unaffected.

Commit 3 — test: pin the contract

  • 5 new tests in src/core/observability.rs covering: canonical Sentry-body match, sibling op-name match, Invalid API key without HTTP 401, negative case (unrelated 401 stays unclassified), negative case (composio-direct 500 stays unclassified).
  • 4 new tests in src/openhuman/composio/ops_test.rs pinning the classifier + failure-tag (non_2xx) + status-extraction contract on the canonical body, plus a 500-doesn't-demote regression test.

Rejected alternatives

  • Stateful auth-state machinery that pauses the 5 s UI poll on direct-mode 401. Triples PR size and the classifier alone fully solves the Sentry-noise leak. Defer to a follow-up if poll-volume becomes the next concern.
  • Dropping the [composio-direct] log line entirely. Hurts debuggability when users report "integrations missing". Demoting to debug via the expected-kind branch is the right trade.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/coverage.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge.
  • N/A: behaviour-only change to observability classifier + symmetric error routing. No new product features in the coverage matrix.
  • N/A: no feature IDs map to this fix — observability/classifier hygiene + symmetric error routing only.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • N/A: this does not touch release-cut surfaces — server-side error routing + classifier ladder only.
  • N/A: Sentry-only triage (self-hosted sentry.tinyhumans.ai issue TAURI-RUST-X9); no GitHub issue to close. Sentry-Issue: header in ## Related so the post-merge resolver hook can flip it.

Impact

  • Runtime/platform: Rust core (src/openhuman/composio/*, src/core/observability.rs). No frontend / Tauri shell changes. Desktop only.
  • Performance: nil (one extra closure call per error path; never on the happy path).
  • Security: nil (no new logging surface for credentials; the log line already exists, only its level changes).
  • Migration / compat: nil (no schema, no API change, no user-visible UX change).
  • Sentry impact: eliminates the dominant tauri-rust project leak (15.7 k events / 22 h from a single user) and pre-empts the same shape from every other direct-mode call site that was bypassing the classifier.

Related

  • Closes:
  • Follow-up PR(s)/TODOs: backend branch of composio::periodic::run_one_tick also lacks the report_composio_op_error hook on its list_connections (backend) arm. Out of scope here (plan was direct-only) but worth a follow-up issue.

Sentry-Issue: TAURI-RUST-X9


AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A: triage driven from self-hosted Sentry (sentry.tinyhumans.ai) not Linear.
  • URL: N/A: see Sentry-Issue header above.

Commit & Branch

  • Branch: fix/sentry-1166-composio-direct-auth-symmetry
  • Commit SHA: b21d43be (tip — 3 micro-commits ahead of upstream/main@c204a53d)

Validation Run

  • N/A: no app/ changes in this PR (Rust core + observability only).
  • N/A: no TypeScript changes in this PR.
  • Focused tests: cargo test -p openhuman --lib openhuman::composio::ops (61 passed), cargo test -p openhuman --lib core::observability (84 passed), cargo test -p openhuman --lib openhuman::composio::periodic (6 passed), cargo test -p openhuman --lib openhuman::composio::tools (31 passed).
  • Rust fmt/check (if changed): cargo fmt --check clean; cargo check --manifest-path Cargo.toml clean.
  • Tauri fmt/check (if changed): cargo check --manifest-path app/src-tauri/Cargo.toml clean (no Tauri-shell changes in this PR; checked for parity).

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: direct-mode Composio HTTP 401 / Invalid API key errors no longer reach Sentry. They surface in debug logs and the existing user-visible "not connected" UI state remains the actionable surface for the user.
  • User-visible effect: zero functional change. Settings UI continues to show direct-mode connections as not-connected when the user's API key is invalid (same as before this PR). Only Sentry stops capturing the noise.

Parity Contract

  • Legacy behavior preserved: backend-mode error routing untouched. The same report_composio_op_errorreport_error_or_expectedexpected_error_kind pipeline that the backend branch already used is now applied uniformly to the direct branch.
  • Guard/fallback/dispatch parity checks: pre-existing report_composio_op_error pinning tests at ops_test.rs:1376+ still pass. The new arm in is_provider_user_state_message reuses the existing ExpectedErrorKind::ProviderUserState variant — no enum surface change, no dispatcher branches changed.

Summary by CodeRabbit

  • Bug Fixes

    • More accurate detection/classification of Composio direct-mode authentication failures (now limited to direct-mode anchors with 401/Invalid API key patterns).
    • Direct-mode errors are now routed through the same reporting path as backend-mode errors for consistent observability and reporting.
  • Tests

    • Added tests covering direct-mode auth rejection shapes, variants, negatives, and error-classification behavior.

Review Change Stack

@oxoxDev oxoxDev requested a review from a team May 22, 2026 06:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

Composio direct-mode authentication failures marked with [composio-direct] and containing HTTP 401 or Invalid API key are classified as ProviderUserState; the composio op error reporter is exposed and invoked from direct-mode ops, periodic, and tool paths. Tests validate positive and negative message shapes.

Changes

Composio Direct-Mode Auth Observability

Layer / File(s) Summary
Direct-mode auth failure detection
src/core/observability.rs
is_provider_user_state_message matches [composio-direct] plus HTTP 401 or Invalid API key, returning ProviderUserState; unit tests added for canonical, variant, and negative cases.
Error reporting helper visibility
src/openhuman/composio/ops.rs
report_composio_op_error visibility changed to pub(super) so direct-mode branches can call the shared reporting path.
Direct-mode error reporting across operations
src/openhuman/composio/ops.rs, src/openhuman/composio/periodic.rs, src/openhuman/composio/tools.rs, src/openhuman/composio/ops_test.rs
Direct-mode error paths for list_connections, authorize, and list_tools (including prefetch), periodic tick, and the ComposioListConnections tool now call report_composio_op_error before returning formatted errors. Tests assert classification as ProviderUserState for direct-mode 401 Invalid API key, non_2xx tagging, extract_backend_returned_status returns None, and direct-mode 500 remains unclassified.
sequenceDiagram
  participant Client as Client (direct mode)
  participant ComposioOp as composio op (list/authorize/tools)
  participant Reporter as report_composio_op_error
  participant Classifier as is_provider_user_state_message
  participant Sentry as Sentry/Observability

  Client->>ComposioOp: invoke direct operation
  ComposioOp->>Reporter: report_composio_op_error(operation, &error)
  Reporter->>Classifier: pass error message for classification
  Classifier->>Sentry: classification result (e.g., ProviderUserState) forwarded
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus
  • senamakel

Poem

🐰 I hopped through logs with a careful glance,
Found [composio-direct] in the error dance,
Routed reports to the watcher near,
So invalid keys are clearly clear,
A little rabbit cheers—observability is here!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing observability and Composio direct-mode 401/Invalid API key classification to reduce Sentry noise.
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.

✏️ 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 22, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented May 22, 2026

Heads-up to reviewers: the 3 frontend failures on this PR (Frontend Coverage, test / Frontend Unit Tests, test / i18n Coverage) are pre-existing breakage on main, not introduced by this PR.

Root cause: PR #2378 (Add German locale support, merged 2026-05-21) shipped without 20 keys that PR #2280 (feat(settings): add MCP server configuration panel, 4e5eaa71) added to English. The strict-equality assertion in src/lib/i18n/__tests__/coverage.test.ts:77 and I18nContext.test.tsx:86 fails as a result.

Verification:

This PR's own quality gates are clean:

  • cargo fmt --check, cargo check, all Rust core + tauri checks green
  • Focused tests: cargo test -p openhuman --lib openhuman::composio::ops (61 passed), core::observability (84 passed), openhuman::composio::periodic (6 passed), openhuman::composio::tools (31 passed)
  • PR Submission Checklist parser: 12/12 satisfied
  • All Rust Quality, Type Check, e2e/Linux integration, Smoke install.sh green

Happy to rebase once a German-backfill PR lands on main. Flagging in case anyone wants to pick up that fix in parallel — out of scope for this Sentry triage PR.

Sentry-Issue: TAURI-RUST-X9 (15,732 events / 22h, single user, openhuman@0.54.0)

@M3gA-Mind
Copy link
Copy Markdown
Contributor

@oxoxDev this PR has merge conflicts with main — please rebase/resolve before review.

@oxoxDev oxoxDev force-pushed the fix/sentry-1166-composio-direct-auth-symmetry branch from b21d43b to d1d8f1d Compare May 22, 2026 14:04
oxoxDev added 3 commits May 22, 2026 19:36
…ites (Sentry TAURI-RUST-X9)

Direct mode shipped via PR tinyhumansai#1825 (2026-05-15) but the mode-aware error
paths in `src/openhuman/composio/{ops,tools,periodic}.rs` only routed
the backend branch through `report_composio_op_error`. The
`ComposioClientKind::Direct` branches wrapped the inner error with
`format!("[composio-direct] … failed: {e:#}")` and skipped the
classifier hook entirely, so the dominant failure shape

    `Composio v3 connected_accounts failed: HTTP 401: Invalid API key: ak_…`

bypassed `report_error_or_expected` and leaked to Sentry on every UI
5 s poll plus every server-side `periodic.rs` tick. Sentry TAURI-RUST-X9
captured 15,732 events in ~22 h from a single user.

This commit restores symmetric error routing — each direct-mode
`.map_err` closure now calls `report_composio_op_error("<op>", &e)`
with the same op-name string the backend branch uses. The
`report_composio_op_error` helper visibility is widened from private
to `pub(super)` so `tools.rs` and `periodic.rs` can reuse it without
inlining the classifier logic.

Patch 2 (next commit) adds the matching arm to
`expected_error_kind` so the wire shape demotes to
`ProviderUserState` (info breadcrumb, no Sentry event) instead of
escaping as an unclassified error.

Files touched:
- src/openhuman/composio/ops.rs — direct branches in
  `composio_list_connections`, `composio_authorize`, and both
  `composio_list_tools` call sites (prefetch + `list_tools` itself).
  `report_composio_op_error` visibility widened to `pub(super)`.
- src/openhuman/composio/tools.rs — `ComposioListConnectionsTool::execute`
  direct branch. (The other tool `execute` direct branches either refuse
  the verb or return an empty response with no network call, so no hook
  to add.)
- src/openhuman/composio/periodic.rs — `run_one_tick` direct branch
  on the server-side scheduler.

Sentry-Issue: TAURI-RUST-X9
…as expected user-state (Sentry TAURI-RUST-X9)

Adds a new arm to `is_provider_user_state_message` that recognizes the
direct-mode composio v3 auth-rejection wire shape:

  `[composio-direct] <op> failed: Composio v3 … failed:
   HTTP 401: Invalid API key: ak_…`

The matcher is gated on a combined anchor — the `[composio-direct]`
prefix (added by ops.rs / tools.rs / periodic.rs direct branches in
the previous commit) AND one of (`HTTP 401`, `Invalid API key`). This
discriminates against backend-mode 401s (which carry the `Backend
returned 401` shape and route through the failure-tag flow with
`status="401"`) while still catching every direct-mode call site that
emits the new prefix.

The arm routes to the existing `ProviderUserState` variant — the same
bucket used for the composio toolkit-not-enabled / OAuth-scope cases
— so the wire shape demotes to an info breadcrumb via
`report_expected_message` with `kind="provider_user_state"` instead of
firing a Sentry event.

Together with the symmetric `report_composio_op_error` hooks added in
the previous commit, this closes the leak for Sentry TAURI-RUST-X9
(~15.7 k events / ~22 h on a single user with a bad direct-mode key).

Sentry-Issue: TAURI-RUST-X9
…er + ops routing

Adds five tests in `src/core/observability.rs` and four tests in
`src/openhuman/composio/ops_test.rs` that lock the
TAURI-RUST-X9 (tinyhumansai#1166) leak-fix in place.

## Observability classifier tests

- `classifies_composio_direct_invalid_api_key_as_provider_user_state`
  pins the verbatim Sentry title body
  (`[composio-direct] list_connections failed: Composio v3
  connected_accounts failed: HTTP 401: Invalid API key: ak_VsUvq*****`)
  to `Some(ProviderUserState)` so the dominant wire shape demotes to an
  info breadcrumb.
- `classifies_composio_direct_invalid_api_key_for_other_ops` covers
  the parallel direct-mode op-name prefixes (`list_tools` prefetch,
  `authorize`, `list_tools`) so a future op added to the family doesn't
  silently bypass the matcher.
- `classifies_composio_direct_with_invalid_api_key_only_no_http_401`
  pins the OR semantics of the combined anchor — body matches either
  `HTTP 401` or `Invalid API key`.
- `does_not_classify_unrelated_http_401_as_composio_direct_user_state`
  is the discrimination test: backend-mode 401 (`Backend returned 401
  …`) and unrelated provider 401s (`GitHub API error: HTTP 401`) MUST
  NOT match the new arm.
- `does_not_classify_composio_direct_500_as_user_state` pins that a
  real bug shape (500 with no auth body) still falls through to
  `None`, preserving Sentry signal for genuine backend faults.

## Composio ops tests

- `composio_direct_invalid_api_key_classifies_as_provider_user_state`
  re-asserts the classifier contract from the composio side so a future
  classifier regression surfaces in both crates.
- `composio_direct_invalid_api_key_failure_tag_is_non_2xx` pins the
  failure-tag side of the path: even if the classifier ever stops
  matching, the tag stays `non_2xx` so the `before_send` filter has a
  consistent input.
- `composio_direct_invalid_api_key_extract_status_returns_none` pins
  the contract that `extract_backend_returned_status` only parses the
  integrations-layer `Backend returned <status>` rendering, NOT the
  direct-mode `HTTP 401` shape. Documents the boundary so a future
  extension to cover both shapes comes with its own test.
- `composio_direct_500_does_not_demote` re-asserts the discrimination
  contract from the composio side.

## Spy infrastructure (deviation from plan)

The plan suggested extending an `report_composio_op_error` spy in
ops_test.rs to assert the hook was invoked on the direct branch. No
such spy infrastructure exists in the current codebase — the existing
ops_test pinning tests only exercise `classify_composio_failure_tag`
and `extract_backend_returned_status` directly. Rather than introduce
a Sentry test client + mock plumbing for this single commit, the tests
above pin the **observable contract** (`expected_error_kind` for the
canonical Sentry body + `classify_composio_failure_tag` + status
extraction) which is what the leak-fix actually changes from the
caller's perspective. The direct-branch call sites were verified by
inspection in patch 1 of tinyhumansai#1166 (every `.map_err` on the direct path
now calls `report_composio_op_error(<op>, &e)`).

Sentry-Issue: TAURI-RUST-X9
@oxoxDev oxoxDev force-pushed the fix/sentry-1166-composio-direct-auth-symmetry branch from d1d8f1d to 549a4ad Compare May 22, 2026 14:08
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented May 22, 2026

Rebased onto upstream/main ed3e453b (was c204a53d). Resolved one conflict in composio_authorize direct branch — upstream PR #2469 swapped direct_authorize for direct_authorize_with_meta_guard + rate-limit wrap; merged my report_composio_op_error hook onto the wrapped error so rate-limit classifications also flow through the expected-kind ladder.

New tip: 549a4add (3 GPG-signed commits). Focused tests still green post-rebase:

  • cargo test -p openhuman --lib core::observability → 86 passed
  • cargo test -p openhuman --lib openhuman::composio::ops → 62 passed

PR now MERGEABLE. The 3 frontend failures persist — confirmed pre-existing main breakage per earlier comment; main Test workflow still red on ed3e453b.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/composio/ops.rs`:
- Around line 246-255: The report_composio_op_error calls in the direct-mode
branches are logging the original error before adding the “[composio-direct]”
anchor, so the classifier misses demotion; update each direct-mode error mapping
(the ones wrapping direct_list_connections, authorize, the direct list_tools
prefetch and the direct list_tools call) to first prepend or format the error
with the “[composio-direct]” anchor and then pass that prefixed error into
report_composio_op_error, e.g. create a prefixed_error =
format!("[composio-direct] {e:#}") (or otherwise attach the anchor) and call
report_composio_op_error("list_connections", &prefixed_error) before returning
the formatted error string; apply the same pattern to the authorize and
list_tools direct branches referenced in the comment.

In `@src/openhuman/composio/periodic.rs`:
- Around line 178-190: The periodic Direct branch (ComposioClientKind::Direct)
currently calls super::ops::report_composio_op_error with the raw error `e`,
which bypasses the composio-direct classifier; instead, build a rendered message
containing the `[composio-direct]` anchor (e.g. let msg =
format!("[composio-direct] list_connections (direct): {e:#}")), call
super::ops::report_composio_op_error("list_connections", &msg) with that string,
and then return Err(msg) (i.e. propagate the same rendered message via the
map_err return) so the periodic 401s are classified the same as UI failures;
update the direct_list_connections(direct).await.map_err closure to construct
and use that anchored message rather than passing `e` directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eea3e84a-6e18-472c-9876-5c17f589b4a2

📥 Commits

Reviewing files that changed from the base of the PR and between d1d8f1d and 549a4ad.

📒 Files selected for processing (5)
  • src/core/observability.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/ops_test.rs
  • src/openhuman/composio/periodic.rs
  • src/openhuman/composio/tools.rs

Comment on lines +246 to +255
let resp = direct_list_connections(&direct).await.map_err(|e| {
// [#1166 / Sentry TAURI-RUST-X9] Restore symmetric error
// routing for the direct-mode branch. Without this hook the
// direct-mode 401 ("Invalid API key …") wire shape bypassed
// `report_error_or_expected` and leaked ~15.7k events in ~22h
// — same UI 5 s poll + `periodic.rs` tick that the
// backend branch (line ~266) was already classifying.
report_composio_op_error("list_connections", &e);
format!("[composio-direct] list_connections failed: {e:#}")
})?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Direct-mode errors are reported before the [composio-direct] anchor is added.

At Line 253, Line 354, Line 501, and Line 536, report_composio_op_error(...) receives unprefixed errors, but the new classifier arm requires [composio-direct]. This means those 401/invalid-key failures can still miss demotion and leak to Sentry.

✅ Suggested fix pattern
- let resp = direct_list_connections(&direct).await.map_err(|e| {
-     report_composio_op_error("list_connections", &e);
-     format!("[composio-direct] list_connections failed: {e:#}")
- })?;
+ let resp = direct_list_connections(&direct).await.map_err(|e| {
+     let rendered = format!("[composio-direct] list_connections failed: {e:#}");
+     report_composio_op_error("list_connections", &rendered);
+     rendered
+ })?;

Apply the same pattern to direct authorize, direct list_tools prefetch, and direct list_tools call.

Also applies to: 347-355, 497-503, 532-538

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/composio/ops.rs` around lines 246 - 255, The
report_composio_op_error calls in the direct-mode branches are logging the
original error before adding the “[composio-direct]” anchor, so the classifier
misses demotion; update each direct-mode error mapping (the ones wrapping
direct_list_connections, authorize, the direct list_tools prefetch and the
direct list_tools call) to first prepend or format the error with the
“[composio-direct]” anchor and then pass that prefixed error into
report_composio_op_error, e.g. create a prefixed_error =
format!("[composio-direct] {e:#}") (or otherwise attach the anchor) and call
report_composio_op_error("list_connections", &prefixed_error) before returning
the formatted error string; apply the same pattern to the authorize and
list_tools direct branches referenced in the comment.

Comment on lines +178 to +190
ComposioClientKind::Direct(direct) => {
direct_list_connections(direct).await.map_err(|e| {
// [#1166 / Sentry TAURI-RUST-X9] The server-side periodic
// tick re-renders the same v3 `/connected_accounts` 401
// shape that `ops::composio_list_connections` emits, so
// route it through the observability classifier too.
// Without this, the tick-side 401s leak as unclassified
// Sentry events even when the UI poll's identical failure
// is correctly classified.
super::ops::report_composio_op_error("list_connections", &e);
format!("list_connections (direct): {e:#}")
})?
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Periodic direct-mode reporting still bypasses the new composio-direct matcher.

At Line 187, report_composio_op_error is invoked with raw e, which may not carry the [composio-direct] anchor required by the new classifier. Prefix the rendered direct-mode message before reporting so periodic 401 invalid-key errors are demoted consistently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/composio/periodic.rs` around lines 178 - 190, The periodic
Direct branch (ComposioClientKind::Direct) currently calls
super::ops::report_composio_op_error with the raw error `e`, which bypasses the
composio-direct classifier; instead, build a rendered message containing the
`[composio-direct]` anchor (e.g. let msg = format!("[composio-direct]
list_connections (direct): {e:#}")), call
super::ops::report_composio_op_error("list_connections", &msg) with that string,
and then return Err(msg) (i.e. propagate the same rendered message via the
map_err return) so the periodic 401s are classified the same as UI failures;
update the direct_list_connections(direct).await.map_err closure to construct
and use that anchored message rather than passing `e` directly.

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