fix(connectors): repair Email Triage Google grant + actionable token errors (#1592)#1599
Conversation
… 401 actionable, connect-time secret check (#1592) Four related root causes, all verified on-box (t-nx-strx-halo): 1. builtin:email grants orphaned by #1520 hub-rename are now migrated to installed:email at UI-server startup (grants.py + server.py). Idempotent: existing installed:email entries are preserved. 2. Email tool ConnectorsError envelopes now go through format_connector_error() instead of str(exc), so the NOT_CONNECTED:/AGENT_NOT_GRANTED:/AUTH_REQUIRED: prefix is present and the in-chat CTA in EmailConnectCta.tsx fires. 3. _refresh_token now has an explicit 401 branch: no client_secret → ConfigurationError (actionable: re-enter credentials); secret present → AuthRequiredError REAUTH_REQUIRED (actionable: reconnect). Previously both landed in a generic ConnectorsError. 4. OAuthPkceHandler.configure() validates the provider client_secret before starting the PKCE flow, so a bad config surfaces at connect time (AC5) rather than as a cryptic 401 on first email triage. 21 new unit tests (4 test files) cover every changed path; 10 vitest tests cover the isAuthRequiredMessage CTA detector. Auto-confer-on-connect is intentionally not added (AC1 satisfied by CTA + migration + existing Settings grant panel). Closes #1592
…handlers EmailSummarizeError (a RuntimeError) was caught alongside ConnectorsError and routed through format_connector_error, which mapped it to "UNEXPECTED_ERROR: EmailSummarizeError: …" instead of the plain str(exc) the user and LLM see. Split the handlers in summarize_tools.py and read_tools.py so each error type produces the correct output. Also fixes docstring contradiction in grants.py (said "left as-is" but code correctly removes the legacy key), narrows the silent except in oauth_pkce._validate_provider_secret to (ConfigurationError, KeyError), and strengthens the migration test to assert the stale builtin:email key is removed.
Review — fix(connectors): repair Email Triage Google grant + actionable token errors (#1592)Approve with suggestions. This is a well-structured, well-tested fix that correctly attacks all four root causes behind the broken Email Triage grant flow. The changes are scope-clean, the error paths fail loudly with actionable messages, and the new test coverage is genuinely strong (migration idempotency, the 401 secret-present/absent split, the envelope-prefix contract, and an explicit "this is the bug" regression test). The one thing worth confirming before merge: the startup migration is wired into the UI server only, so CLI-only users with an orphaned Issues🟡 Legacy-grant migration runs on UI startup only, not CLI ( 🟢 The actionable 401 reauth message is discarded when surfaced through the email CTA ( 🟢 PR description says the 401 error "names the account"; the code names only the provider ( 🟢 Broad Strengths
VerdictApprove with suggestions. No blocking issues. Please confirm the UI-only migration wiring is intentional (🟡 above) — that's the only item that could leave a class of users still broken. The two 🟢 message-fidelity notes are polish; the description tweak is trivial. |
|
🟡 try:
err_payload = response.json()
except Exception:
err_payload = {}This violates CLAUDE.md's explicit prohibition on
|
kovtcharov-amd
left a comment
There was a problem hiding this comment.
Approve. Thorough fix with two correctly-diagnosed root causes and strong regression coverage.
Strengths:
- str(exc) -> format_connector_error(exc) is the right fix; guarantees the AGENT_NOT_GRANTED:/NOT_CONNECTED:/AUTH_REQUIRED: prefix the frontend CTA keys on, applied consistently across tool modules.
- Good catch splitting EmailSummarizeError out of the ConnectorsError except branch — otherwise it would have been mangled into 'UNEXPECTED_ERROR: EmailSummarizeError: …'. Guarded by tests.
- Migration is idempotent and conservative (never clobbers an existing installed:email grant), held under _write_lock, with a no-op fast path.
- 401 handling is correctly placed between the existing 400 branch and the generic !=200 branch, and distinguishes 'secret missing -> ConfigurationError' from 'secret present but rejected -> reauth'.
Non-blocking points to consider:
- Migration is only wired into ui/server.py; the docstring mentions the CLI too but the CLI path isn't wired. Acceptable since #1592 is UI-scoped — consider wiring the CLI entrypoint or tightening the docstring.
- Wording drift: new messages say 'Settings -> Connections' while other parts (and CTA fixtures) say 'Connectors'. The fuzzy matcher accepts both, but pick one for consistency.
- _validate_provider_secret hardcodes the google-only early return; a comment or capability flag would age better when Microsoft needs a secret.
|
This PR merged before these two review items were addressed, so they're tracked as follow-ups:
|
Connecting a Google account in the Agent UI left the Email Triage agent unusable: triage failed with a vague "no permissions" message that could only be cleared from the CLI, and after the CLI grant, token refresh failed with a generic "technical issue." Two root causes — the #1520 hub migration renamed the per-agent grant key
builtin:email→installed:email(orphaning every existing grant), and the in-chat "grant access" CTA never fired because the email tools emittedstr(exc)instead of theAGENT_NOT_GRANTED:-prefixedformat_connector_error. After this change: legacy grants migrate automatically on startup; the in-chat CTA fires and points the user at the existing in-UI grant control (no CLI step); token-refresh 401s become actionable (names the account, distinguishes reconnect vs. server-side client-secret); and the OAuth client secret is validated at connect time so a "connected" account that can't refresh is caught immediately.Closes #1592
Test plan
pytest tests/unit/connectors/— 534 passed, 5 skipped (incl. new grant-migration, token-401, secret-validation, and email-tool-envelope regression tests)python util/lint.py --allcd src/gaia/apps/webui && npm run build+ vitest (EmailConnectCtafires on theAGENT_NOT_GRANTED:prefix)builtin:emailgrant migrates toinstalled:emailon restart — pending live OAuthNote: auto-confer-on-connect was intentionally NOT added (consent scope) — the in-UI one-click grant + migration satisfy the AC without blanket-granting every agent.