Skip to content

fix(observability,embeddings): demote Ollama embed user-config rejections (Sentry TAURI-RUST-XS + MA/KM/GX)#2612

Open
oxoxDev wants to merge 2 commits into
tinyhumansai:mainfrom
oxoxDev:fix/sentry-xs-ollama-classifier-arm
Open

fix(observability,embeddings): demote Ollama embed user-config rejections (Sentry TAURI-RUST-XS + MA/KM/GX)#2612
oxoxDev wants to merge 2 commits into
tinyhumansai:mainfrom
oxoxDev:fix/sentry-xs-ollama-classifier-arm

Conversation

@oxoxDev
Copy link
Copy Markdown
Contributor

@oxoxDev oxoxDev commented May 25, 2026

Summary

Problem

Self-hosted Sentry is collecting ~376 events for TAURI-RUST-XS with the canonical body:

ollama embed failed with status 400 Bad Request: {"error":"invalid model name"}

These come from users who configured the embedding provider with a chat / vision model id (sometimes with a temperature suffix like qwen3-vl:4b@0.7) that Ollama parses as malformed. The UI already surfaces an actionable error in the Settings → Embeddings page; Sentry has no remediation path because the request was malformed by the user's input, not by our code.

Three sibling wire shapes — OPENHUMAN-TAURI-MA, OPENHUMAN-TAURI-KM, OPENHUMAN-TAURI-GX — share the same "user-config rejection" semantics but escape capture today because the classifier ladder has no arm for them. PR #2216 (Lane K, merged 2026-05-20) routed the call sites through report_error_or_expected but explicitly deferred adding the matcher arm to a follow-up gated on PR #2063 + #2188 merging. Both gating PRs merged 2026-05-19/20 and the follow-up was never opened — this PR closes that gap.

The three existing pinning tests at src/openhuman/embeddings/ollama_tests.rs:339-378 (named *_current_state_unclassified) lock the current escape-to-Sentry state with comments pointing at this exact follow-up.

Solution

Add a single matcher is_ollama_user_config_rejection(lower: &str) -> bool to src/core/observability.rs that anchors on the ollama embed failed / ollama embeddings opted-in prefixes plus one of three body anchors:

  1. "invalid model name" (XS — 400 Bad Request)
  2. "model \"…\" not found" (MA / KM — 404 Not Found, pull-required)
  3. "opted-in but daemon unreachable at" (GX — daemon not running)

Wire the matcher into expected_error_kind after is_loopback_unavailable and before is_network_unreachable_message. The GX wire shape contains localhost: (a loopback host) but no Connection refused (os error N) marker, so it would otherwise fall through the loopback arm. Inserting before the network-unreachable arm gives the Ollama prefix anchors first dibs.

Route the matches to the existing ExpectedErrorKind::ProviderUserState variant (the same bucket holding the composio / gmail / OAuth user-state errors) rather than introducing a new variant. The demotion semantics (drop to info log, skip Sentry capture) are identical and adding a per-provider variant would balloon the enum without changing behavior.

No call-site changes — PR #2216 already routed src/openhuman/embeddings/ollama.rs:247-279 through report_error_or_expected. Verified the route is still in place on this branch.

Tests:

  • src/core/observability.rs::tests::classifies_ollama_user_config_rejections — five positive wire shapes (XS canonical, XS with temperature-suffix model, MA, KM, GX).
  • src/core/observability.rs::tests::does_not_classify_unrelated_ollama_errors_as_user_config — four negatives (500 server error, parse failure, dimension mismatch, unrelated model "…" not found without the ollama embed prefix).
  • src/openhuman/embeddings/ollama_tests.rs — three flipped pinning tests ({ma,km,gx}_wire_shape_classifies_as_provider_user_state), two new XS positives, two new negatives (ollama_500_wire_shape_stays_unexpected, ollama_dimension_mismatch_stays_unexpected). The GP test (LocalAiCapabilityUnavailable) and parse-error escape test are retained verbatim.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • N/A: Diff coverage ≥ 80% — behavior-only classifier hygiene; every changed line in is_ollama_user_config_rejection and the wiring is exercised by the new positive + negative unit tests in both observability::tests and embeddings::ollama::tests.
  • N/A: Coverage matrix updated — behavior-only change to an existing Sentry-noise-suppression code path; no new user-facing feature row to add.
  • N/A: All affected feature IDs from the matrix are listed — no matrix rows touched.
  • No new external network dependencies introduced (no network calls in this PR; all tests are pure-function classifier checks)
  • N/A: Manual smoke checklist updated — no release-cut surface touched; this is a pure observability-classifier change with no user-visible behavior.
  • N/A: Linked issue closed via Closes #NNN — Sentry-only triage; the Sentry-Issue: headers in ## Related provide the trail.

Impact

  • Runtime: Desktop (macOS / Windows / Linux). No iOS / mobile / web / CLI surface touched.
  • User-visible behavior: Zero. The UI already surfaces these errors via toast and the Settings → Embeddings warning; this PR only changes what Sentry sees (drops info breadcrumb instead of an error event).
  • Performance: Negligible. Three additional lower.contains(...) substring checks per classification, all short-circuited on the ollama embed / ollama embeddings opted-in prefix.
  • Security: No new attack surface. The matcher operates on already-redacted error messages from report_error_or_expected.
  • Migration / compatibility: None. Adds matcher arms only; no enum variants, no public API changes.
  • Sentry impact: Closes TAURI-RUST-XS (~376 events) plus retires the deferred MA/KM/GX gap. Once this PR ships, the next release should see those wire shapes stop reaching Sentry. Post-merge cleanup will flip TAURI-RUST-XS to resolved and bookmark MA/KM/GX for the 7-day regression-watch window.

Related

CI note — pre-existing main FE failure

Three frontend jobs (Frontend Coverage, Frontend Unit Tests, i18n Coverage) are red on upstream/main since 2026-05-21 due to PR #2378 (German locale missing 20 MCP-Server keys). This PR touches zero frontend files (Rust-only: src/core/observability.rs + src/openhuman/embeddings/ollama_tests.rs) and inherits the failure. PR #2481 merged through the same state — please apply the same precedent here.


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

Linear Issue

  • Key: N/A
  • URL: N/A — Sentry-only triage, no Linear ticket

Commit & Branch

  • Branch: fix/sentry-xs-ollama-classifier-arm
  • Commit SHA: b4bd3da26a342fb4a4051083442db93424668987

Validation Run

  • N/A: pnpm --filter openhuman-app format:check — no frontend files touched
  • N/A: pnpm typecheck — no frontend files touched
  • Focused tests: cargo test --lib core::observability (88 passed) + cargo test --lib openhuman::embeddings::ollama (36 passed)
  • Rust fmt/check (if changed): cargo fmt --check clean; cargo check --manifest-path Cargo.toml clean (pre-existing warnings only); cargo clippy --lib no new warnings on changed code
  • N/A: Tauri fmt/check (if changed): no Tauri files touched

Validation Blocked

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

Behavior Changes

  • Intended behavior change: Stop reporting four wire shapes of Ollama embed user-config rejections (XS / MA / KM / GX) to Sentry; demote to info breadcrumb instead.
  • User-visible effect: None. The Settings → Embeddings page and toast notifications already surface the actionable error to the user; this PR only changes what reaches the observability backend.

Parity Contract

  • Legacy behavior preserved: All other ollama embed failures (500 server errors, parse failures, dimension mismatches, transport-layer connect failures) continue to reach Sentry. Anchored matcher on the ollama embed prefix prevents collisions with unrelated 400/404 error envelopes elsewhere in the codebase.
  • Guard/fallback/dispatch parity checks: Unit tests cover the substring-anchor isolation (does_not_classify_unrelated_ollama_errors_as_user_config). The matcher routes to the existing ProviderUserState variant — report_expected_message already has the correct demotion path for that variant.

Duplicate / Superseded PR Handling

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved error detection and classification for Ollama embedding services, including invalid model names, unavailable models, and unreachable daemon states.
  • Tests

    • Extended test coverage for Ollama error classification to ensure accurate differentiation between user configuration issues and system errors.

Review Change Stack

oxoxDev added 2 commits May 25, 2026 13:55
Adds a dedicated matcher in the Sentry classifier ladder for Ollama
embed user-config rejections. Routes four wire shapes to the existing
ProviderUserState bucket:

- TAURI-RUST-XS (~376 events, self-hosted Sentry): user pointed the
  embedder at a chat / vision model id, sometimes with a temperature
  suffix (`qwen3-vl:4b@0.7`) Ollama parses as malformed. 400 Bad
  Request with `{"error":"invalid model name"}`.
- OPENHUMAN-TAURI-MA / -KM (deferred follow-up from PR tinyhumansai#2216): user
  configured a model the local daemon hasn't pulled. 404 Not Found
  with `{"error":"model \"<id>\" not found, try pulling it first"}`.
- OPENHUMAN-TAURI-GX: user opted into Ollama embeddings but the
  daemon isn't running. Wire shape:
  `ollama embeddings opted-in but daemon unreachable at <url>; falling
  back to cloud embeddings for this session`.

All four are user-config: wrong model id, model not pulled, or daemon
not started. The UI already surfaces actionable errors via toast /
settings warning; Sentry has no remediation path.

Wiring: insert the matcher in `expected_error_kind` after
`is_loopback_unavailable` and before `is_network_unreachable_message`
so the `ollama embed` / `ollama embeddings opted-in` prefix anchors
fire before the broader transport-failure classifiers. Routes to the
existing ProviderUserState variant (matching composio / gmail / OAuth
user-state errors) rather than introducing a new enum variant — the
demotion semantics are identical.

Unit tests cover the four positive wire shapes (with one variant
carrying the temperature-suffix model id) plus four negative shapes
(500 server error, parse failure, dimension mismatch, unrelated
`model "…" not found` outside the Ollama embed prefix).
…re shapes

Flips the three deferred pinning tests from PR tinyhumansai#2216 (Lane K follow-up)
to assert the demotion now lands, and adds two new positive XS cases
plus a dimension-mismatch negative.

- `ma_wire_shape_classifies_as_provider_user_state` (was
  `ma_wire_shape_current_state_unclassified`) — 404 model-not-found
  pull-required.
- `km_wire_shape_classifies_as_provider_user_state` (was
  `km_wire_shape_current_state_unclassified`) — same shape, `:latest`
  tag variant.
- `gx_wire_shape_classifies_as_provider_user_state` (was
  `gx_wire_shape_current_state_unclassified`) — daemon-unreachable
  opt-in state.
- `xs_wire_shape_classifies_as_provider_user_state` (new) — canonical
  TAURI-RUST-XS 400 invalid-model-name wire shape.
- `xs_temperature_suffix_model_classifies_as_provider_user_state`
  (new) — XS variant where the user pasted a model id with a
  temperature suffix like `qwen3-vl:4b@0.7`.
- `ollama_500_wire_shape_stays_unexpected` (new) — server-side bug
  must still reach Sentry.
- `ollama_dimension_mismatch_stays_unexpected` (new) — dim mismatch
  is a real desync bug, must still reach Sentry.
- `gp_wire_shape_classifies` and `ollama_parse_error_wire_shape_stays_unexpected`
  retained verbatim — locks GP demotion + parse-error escape unchanged.

These tests verify the integration end-to-end: the embed call site
already routes through `report_error_or_expected` (added by PR tinyhumansai#2216),
and the classifier now matches the wire shapes the call site emits.
@oxoxDev oxoxDev requested a review from a team May 25, 2026 08:27
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 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: 5d887ba8-9491-4e84-b05f-7683740be03d

📥 Commits

Reviewing files that changed from the base of the PR and between 7ac685e and b4bd3da.

📒 Files selected for processing (2)
  • src/core/observability.rs
  • src/openhuman/embeddings/ollama_tests.rs

📝 Walkthrough

Walkthrough

The PR improves Ollama embedding error classification by adding a specialized detector is_ollama_user_config_rejection that recognizes three user-configuration rejection patterns (invalid model name, missing model pull, daemon unreachable) and routes them to ProviderUserState before generic matchers. Tests validate correct classification of these Ollama rejections and ensure unrelated Ollama errors remain unclassified.

Changes

Ollama User-Config Rejection Classification

Layer / File(s) Summary
Ollama user-config rejection detector and routing
src/core/observability.rs
Adds is_ollama_user_config_rejection helper implementing substring-based detection for Ollama embed 400 invalid model, 404 model-not-pulled, and daemon-unreachable patterns, integrates it into expected_error_kind as an early classification branch to route to ProviderUserState, and includes unit tests validating correct classification of Ollama user-config rejections and false-positive rejection of unrelated Ollama errors.
Ollama embed error shape classification alignment
src/openhuman/embeddings/ollama_tests.rs
Updates test expectations for MA/KM 404 "model not found", XS "invalid model name" (including temperature-suffix variant), and GX "daemon unreachable" wire shapes to assert classification as ProviderUserState; adds tests ensuring 500 "model crashed" and "dimension mismatch" errors return None to prevent false demotion to user-state errors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • tinyhumansai/openhuman#2216: Routes Ollama/OpenAI embed failures through report_error_or_expected, which depends on expected_error_kind that this PR updates.
  • tinyhumansai/openhuman#1795: Also modifies expected_error_kind to route additional provider-shaped errors to ProviderUserState with new/earlier matchers.
  • tinyhumansai/openhuman#2533: Changes Ollama embedding 404-missing-model error formatting, coupled with this PR through error-message wire-shape classification.

Suggested labels

bug

Suggested reviewers

  • graycyrus
  • senamakel
  • M3gA-Mind

Poem

🐰 Ollama's errors now find their true home,
User rejections no longer roam,
With anchored checks, both broad and lean,
We classify what's seen and unseen,
No more false positives in the storm!

🚥 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 directly and specifically describes the main change: adding a classifier to demote Ollama embed user-config rejections to ProviderUserState, with precise Sentry issue identifiers and affected patterns.
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 the bug label May 25, 2026
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented May 25, 2026

CI status

Two failures on the latest run, both unrelated to this PR's diff (src/core/observability.rs + src/openhuman/embeddings/ollama_tests.rs):

  1. e2e / E2E (Linux / Appium Chromium) — same job is failing on upstream/main (last main push run 26391619635 at 2026-05-25T08:39:13Z also red). Inherited; no action in this PR.

  2. Rust Core Coverage (cargo-llvm-cov) — single test failure: openhuman::memory::ops::documents::tests::direct_document_handlers_roundtrip_through_namespace panics with document namespace/key cannot contain personal identifiers. The PII guard (safety::pii::has_likely_pii, added in PR test(memory): extend memory coverage across retrieval and tooling #2574a47740d4) flags the UUID-bearing namespace memory-docs-direct-{uuid} used by the test. This is a memory-domain regression independent of the Ollama classifier change here — happy to spin a follow-up issue if useful, but it shouldn't block this PR.

Rust Core Tests + Quality (the non-llvm-cov variant of the same test suite) passes on this PR, confirming the failure is specific to the cargo-llvm-cov harness on the documents test, not the classifier change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant