fix(channels): classify empty-response, 4xx and vision chat errors with actionable copy (#3119)#3199
Conversation
|
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)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds three typed inference error classifications (empty_response, capability_unsupported, provider_request_rejected) to classify_inference_error, introduces two helper predicates, captures and logs classification results, and adds tests including ordering-regression cases to preserve prior model_unavailable behavior. ChangesProvider Error Classification Expansion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/openhuman/channels/providers/web_errors.rs (1)
344-367: ⚡ Quick winAdd structured debug/trace breadcrumbs on the new classification paths.
These new branches change user-facing error routing but currently emit no diagnostics. A lightweight, redacted
tracing::debug!/trace!event with stable fields (error_type,source,provider, correlation id) would materially improve triage.As per coding guidelines "In Rust, default to verbose diagnostics on new/changed flows using
log/tracingatdebug/tracelevels with stable grep-friendly prefixes and correlation fields".Also applies to: 527-568
🤖 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/channels/providers/web_errors.rs` around lines 344 - 367, The new classification arm that returns a ClassifiedError for is_empty_provider_response_text lacks any diagnostic breadcrumb; add a concise tracing::debug! (or trace!) call adjacent to where the ClassifiedError is constructed (the branch using is_empty_provider_response_text and the ClassifiedError with error_type "empty_response") emitting stable fields error_type="empty_response", source="agent_loop", provider=(Option<String> redacted or None), and the request correlation id (pull from the available context variable used for correlation), ensuring values are redacted/stable-friendly and do not include full provider payloads; apply the same pattern to the other new classification branches around the code that constructs ClassifiedError (e.g., the arm near lines 527-568) so each classification emits a debug-level breadcrumb with the same field names.
🤖 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.
Nitpick comments:
In `@src/openhuman/channels/providers/web_errors.rs`:
- Around line 344-367: The new classification arm that returns a ClassifiedError
for is_empty_provider_response_text lacks any diagnostic breadcrumb; add a
concise tracing::debug! (or trace!) call adjacent to where the ClassifiedError
is constructed (the branch using is_empty_provider_response_text and the
ClassifiedError with error_type "empty_response") emitting stable fields
error_type="empty_response", source="agent_loop", provider=(Option<String>
redacted or None), and the request correlation id (pull from the available
context variable used for correlation), ensuring values are
redacted/stable-friendly and do not include full provider payloads; apply the
same pattern to the other new classification branches around the code that
constructs ClassifiedError (e.g., the arm near lines 527-568) so each
classification emits a debug-level breadcrumb with the same field names.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bf1df762-e9b7-4493-af18-c333bf0c8d23
📒 Files selected for processing (2)
src/openhuman/channels/providers/web_errors.rssrc/openhuman/channels/providers/web_tests.rs
…y breadcrumb (tinyhumansai#3199 review) Address CodeRabbit review on PR tinyhumansai#3199: - is_provider_request_rejected_text now matches only inside the provider error envelope ("<provider> API error (4xx …)") instead of bare 400/404/ 422 substrings, so unrelated errors carrying those digits (token counts, byte offsets, timestamps) can't be misclassified as request rejections. - Add a single redacted log::debug! breadcrumb at the end of classify_inference_error covering every arm (error_type/source/retryable/ provider) per the verbose-diagnostics guideline. Raw error text is left to the caller's existing warn-level log to avoid duplicating provider payload.
|
Re: the diagnostics nitpick (new classification arms had no breadcrumb) — addressed in 9bf0611. Added a single redacted |
…tionable copy (tinyhumansai#3119) The chat error classifier dead-ended every unrecognized provider/agent failure on the generic 'Something went wrong' message, leaving users unable to self-remediate. Add three arms to classify_inference_error: - empty_response: the dominant cause (model returns an empty completion; AgentError::EmptyProviderResponse flattened to a String). Retryable, with a switch-model remedy. Placed next to max_iterations. - provider_request_rejected: un-claimed provider 4xx (400/404/422), with the scrubbed upstream reason quoted. Ordered after the config-rejection and model-unavailable arms so their specific verdicts win first. - capability_unsupported: image sent to a text-only model; non-retryable, points at a vision-capable model. Two predicates mirror the canonical observability/provider checks. Adds 7 tests including three ordering-locks proving the new 4xx arm does not steal invalid-temperature, model-not-found, or DeepSeek reasoning_content cases already claimed by earlier arms.
…y breadcrumb (tinyhumansai#3199 review) Address CodeRabbit review on PR tinyhumansai#3199: - is_provider_request_rejected_text now matches only inside the provider error envelope ("<provider> API error (4xx …)") instead of bare 400/404/ 422 substrings, so unrelated errors carrying those digits (token counts, byte offsets, timestamps) can't be misclassified as request rejections. - Add a single redacted log::debug! breadcrumb at the end of classify_inference_error covering every arm (error_type/source/retryable/ provider) per the verbose-diagnostics guideline. Raw error text is left to the caller's existing warn-level log to avoid duplicating provider payload.
9bf0611 to
cc14c9b
Compare
Summary
classify_inference_error(src/openhuman/channels/providers/web_errors.rs) is a first-match-wins substring chain whose finalelsereturns that generic copy — any error matching no arm falls through with zero guidance.Problem
Self-hosted Sentry (
tauri-rust,error_type:inference, live on the current release and staging) shows the top causes funnelling into the generic catch-all:model_unavailablekeywords400 Bad Request,404,422)All collapsed to one dead-end message, so users couldn't tell whether to retry, switch model, drop an attachment, or top up — and support absorbed the difference. Confirmed independently from a teammate report of the same string.
Solution
Three arms added to
classify_inference_error, ordering chosen so none shadows (or is shadowed by) an existing arm:empty_response— placed next tomax_iterations(both are deterministic agent-state outcomes with a specific anchor). Matches the canonical"model returned an empty response"phrase (AgentError::EmptyProviderResponse, flattened to aStringat the native-bus boundary). Retryable; message points at switching model / checking the local provider in Settings → AI → LLM.provider_request_rejected— un-claimed provider400/404/422. Ordered after the provider-config-rejection and model-unavailable arms so their more specific 4xx verdicts (invalid temperature, stale model pin, model-not-found, DeepSeekreasoning_content) still win. Non-retryable; the real upstream reason is surfaced via the existing secret-scrubbed, length-cappedwith_provider_detail.capability_unsupported— image markers sent to a text-only model. Non-retryable; points at a vision-capable model.Two predicates (
is_empty_provider_response_text,is_provider_request_rejected_text) mirror the canonical observability/provider checks, matching the file's existing mirror convention (is_non_retryable_rate_limit_text).This fixes the surface for #3092 / #3119 and most of #3104. The deeper DeepSeek thinking-mode
reasoning_contentround-trip bug (so those turns actually succeed, not just classify nicely) is tracked separately in #3197.Submission Checklist
classify_inference_error_*tests incl. three ordering-locks (invalid-temperature 400, model-not-found 404, DeepSeek reasoning_content 400 all stay on their specific arms, not the new 4xx arm).## Related— N/A: no matrix feature IDs affected.Closes #NNNin the## Relatedsection.Impact
error_typetokens are additive and the frontend keys its recovery UI offretryable/source, not a hardcodederror_typeswitch.sanitize_api_error+ 300-char cap, so no new PII/secret surface."error decoding response body"(transport/parse) and"unknown embedding provider: local"(embeddings misconfig) still route to the generic catch-all — left for a follow-up.Related
reasoning_contentround-trip (deeper provider-builder fix).AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/3119-classify-inference-errors2cb095e6(classifier arms + tests),cc14c9b3(review fixes: envelope-scoped 4xx matcher + classify breadcrumb). Rebased ontoupstream/main@cac09cbc.Validation Run
cargo fmt --all -- --check→ clean.cargo test --lib openhuman::channels::providers::web::tests::classify_inference_error→ 29 passed, 0 failed.web_errors.rs/web_tests.rs(pre-existing crate-wide clippy debt under blanket-D warningsis unrelated and untouched).pnpm typecheck— noapp/srcchange.app/src-taurichange (pre-pushrust:checkstill run).Validation Blocked
command:noneerror:noneimpact:none — pure deterministic string-classification logic; unit tests are the full proof, no app build/smoke required.Behavior Changes
retryable/source/error_typeinstead of the generic apology.Parity Contract
classify_inference_error_*verdicts unchanged (locked by the three ordering-lock tests + the full pre-existing suite staying green).Duplicate / Superseded PR Handling
Summary by CodeRabbit
Bug Fixes
Tests