Skip to content

fix(ai): contain provider setup errors#2411

Open
aqilaziz wants to merge 8 commits into
tinyhumansai:mainfrom
aqilaziz:codex/2363-ai-provider-error-wrap
Open

fix(ai): contain provider setup errors#2411
aqilaziz wants to merge 8 commits into
tinyhumansai:mainfrom
aqilaziz:codex/2363-ai-provider-error-wrap

Conversation

@aqilaziz
Copy link
Copy Markdown
Contributor

@aqilaziz aqilaziz commented May 21, 2026

Summary

  • Contain long AI provider setup errors inside the settings modal
  • Show a short user-facing summary and keep raw provider payloads behind expandable details
  • Add focused coverage for long provider setup failures

Problem

Provider setup failures can include long URLs or JSON-like API payloads that overflow the LLM settings modal.

Solution

  • Normalize setup failures into a short summary plus optional raw detail
  • Redact endpoint URLs from the summary and cap overly long text
  • Render raw details in a wrapped, scrollable disclosure block

Submission Checklist

Required

  • I have formatted the code with pnpm format or an equivalent focused formatter check
  • I have linted the code with pnpm lint or verified the touched code path with focused checks
  • I have run the relevant tests, and they pass
  • I have included or updated tests for the change, or explained why not needed

Coverage

  • Added/updated tests for changed behavior
  • N/A: no coverage matrix row added or removed

Release / Manual Validation

  • Verified provider setup error handling with a focused unit test
  • N/A: settings modal UI-only change, no release checklist update needed

Linear Metadata

  • N/A: this PR is linked to a GitHub issue

Validation

  • prettier --check src/components/settings/panels/AIPanel.tsx src/components/settings/panels/__tests__/AIPanel.test.tsx
  • pnpm test:unit src/components/settings/panels/__tests__/AIPanel.test.tsx
  • git diff --check

Related

Closes #2363

AI Authored PR Metadata

  • Generated with AI assistance and manually validated with focused tests.

Summary by CodeRabbit

  • Style

    • Small UI markup cleanup with no behavioral changes.
  • Tests

    • Added a test covering the OpenAI API-key flow: verifies the credential-rejection alert is shown and remains focused on the user-facing message; adjusted test behavior to simulate a successful async call.
  • Documentation

    • Updated German translations for the mascot custom GIF strings.

Review Change Stack

@aqilaziz aqilaziz requested a review from a team May 21, 2026 04:57
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes a separator in the AI settings panel, updates tests to make setCloudProviderKey resolve and adds a UI test for provider-setup failure, and updates three German mascot custom GIF translation strings.

Changes

AI settings UI & tests

Layer / File(s) Summary
Remove separator/comment in AIPanel
app/src/components/settings/panels/AIPanel.tsx
Deletes an adjacent separator/comment block previously placed after ProviderKeyDialog; no logic or data-flow changes.
Tests: async mock and failure-path UI test
app/src/components/settings/panels/__tests__/AIPanel.test.tsx
Changes setCloudProviderKey mock to mockResolvedValue(undefined) and adds a UI test that rejects listProviderModels, then verifies saving an API key surfaces a generic credential-rejection alert in the dialog.
German i18n: mascot custom GIF strings
app/src/lib/i18n/chunks/de-5.ts
Replaces settings.mascot.customGifError, settings.mascot.customGifHeading, and settings.mascot.customGifLabel translations with updated wording.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • graycyrus

Poem

🐇 I nudged the dash from AIPanel's line,
flipped a mock to finish on time,
a test now frowns when keys go wrong,
translations hummed a tidier song,
hop—patch landed, all seems fine.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive All changes are directly scoped to the provider setup error handling objective. Minor translation updates for the mascot custom GIF feature appear ancillary but are unrelated to the primary error-containment scope. Clarify whether the German translation changes in de-5.ts are intentional updates accompanying this fix, or if they were included by mistake and should be in a separate commit.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(ai): contain provider setup errors' directly summarizes the main change—containing/constraining provider setup errors within the modal UI instead of allowing them to overflow.
Linked Issues check ✅ Passed The PR implements all core coding requirements from #2363: error wrapping/containment in the modal, user-friendly error summaries with normalized detail disclosure, scrollable wrapped raw details, and new unit test coverage for long errors.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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.

🧹 Nitpick comments (3)
app/src/components/settings/panels/AIPanel.tsx (2)

205-225: ⚡ Quick win

Internationalize the fallback error message.

The fallback message on line 207 is hardcoded in English. For consistency with the rest of the codebase (which uses the t() function for i18n), this should be internationalized.

Suggested change
 function formatProviderSetupError(err: unknown): ProviderSetupError {
   const raw = err instanceof Error ? err.message : String(err ?? '');
-  const fallback = 'Provider setup failed. Check the provider settings and try again.';
+  const fallback = t('settings.ai.providerSetupFallbackError');
   if (!raw.trim()) return { summary: fallback };

You'll need to add the translation key to your i18n resources and accept t as a parameter or use the useT() hook if this function is called within a component context. Alternatively, keep the function pure and handle the fallback message at the call sites.

🤖 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 `@app/src/components/settings/panels/AIPanel.tsx` around lines 205 - 225,
formatProviderSetupError currently uses a hardcoded English fallback string;
update it to use i18n by either (A) adding a translation key (e.g.
"provider.setup_failed") to your i18n resources and changing
formatProviderSetupError to accept a t function parameter (e.g.
formatProviderSetupError(err: unknown, t: TFunction)) and call
t('provider.setup_failed') instead of the literal, or (B) keep
formatProviderSetupError pure and remove the fallback here, returning
undefined/detail only and let callers (components using useT()) supply the
localized fallback; ensure ProviderSetupError consumers are updated accordingly
to use t() or useT() and add the new translation key to the locale files.

673-691: ⚡ Quick win

Internationalize the "Details" label.

The "Details" label on line 682 is hardcoded in English. This should use the t() function for internationalization, consistent with other user-facing strings in the component (e.g., line 619 uses t('settings.ai.connectProvider')).

Suggested change
-function ProviderSetupErrorMessage({ error }: { error: ProviderSetupError }) {
+function ProviderSetupErrorMessage({ error }: { error: ProviderSetupError }) {
+  const { t } = useT();
   return (
     <div className="min-w-0 rounded-md border border-red-200 dark:border-red-500/30 bg-red-50 dark:bg-red-500/10 px-3 py-2 text-xs text-red-700 dark:text-red-300">
       <p role="alert" className="font-medium leading-relaxed break-words [overflow-wrap:anywhere]">
         {error.summary}
       </p>
       {error.detail && (
         <details className="mt-2">
-          <summary className="cursor-pointer select-none text-[11px] font-medium text-red-700 dark:text-red-200">
-            Details
-          </summary>
+          <summary className="cursor-pointer select-none text-[11px] font-medium text-red-700 dark:text-red-200">
+            {t('settings.ai.errorDetails')}
+          </summary>
🤖 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 `@app/src/components/settings/panels/AIPanel.tsx` around lines 673 - 691,
ProviderSetupErrorMessage currently hardcodes the "Details" summary text;
replace that literal with a call to the i18n function (t) to match the rest of
the component. In the ProviderSetupErrorMessage component, change the <summary>
content from "Details" to t('<appropriate.key>') (e.g., t('settings.ai.details')
or whatever existing localization key you prefer), import/use the same t
function/context as the parent component so the label is internationalized
consistently.
app/src/components/settings/panels/__tests__/AIPanel.test.tsx (1)

344-366: ⚡ Quick win

Consider explicitly testing the expand/collapse behavior.

The test verifies that the friendly message appears in the alert and the raw error text exists in the dialog, which covers the core functionality. However, line 365 searches the entire dialog for the raw error text without explicitly verifying it's hidden before the user expands the "Details" section.

To more thoroughly test the "raw detail behind details" behavior described in the test name, consider:

  1. Asserting the raw text is not visible before expanding (e.g., checking the <details> element is closed or using a visibility query)
  2. Clicking the "Details" button/control to expand it
  3. Then asserting the raw error text is visible

This would more accurately verify the hide/show interaction rather than just checking that the content exists in the DOM.

✨ Example enhancement
  const alert = await within(dialog).findByRole('alert');
  expect(alert).toHaveTextContent(/Could not reach OpenAI\./i);
  expect(alert).not.toHaveTextContent('invalid_api_key');
- expect(within(dialog).getByText('Details')).toBeInTheDocument();
- expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeInTheDocument();
+ 
+ // Verify raw error is not visible initially (hidden behind details)
+ expect(screen.queryByText(/invalid_api_key_invalid_api_key/)).not.toBeVisible();
+ 
+ // Expand the details section
+ const detailsToggle = within(dialog).getByText('Details');
+ fireEvent.click(detailsToggle);
+ 
+ // Now raw error should be visible
+ await waitFor(() =>
+   expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeVisible()
+ );

Note: Adjust based on how ProviderSetupErrorMessage implements the details disclosure (e.g., <details>/<summary> vs. custom toggle).

🤖 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 `@app/src/components/settings/panels/__tests__/AIPanel.test.tsx` around lines
344 - 366, Update the test in AIPanel.test.tsx to explicitly verify the
expand/collapse behavior: after confirming the friendly alert text, assert the
raw error text is not visible (e.g., query the <details> disclosure or use a
visibility assertion on the raw string
within(dialog).queryByText(/invalid_api_key_invalid_api_key/)). Then simulate
the user expanding the Details control (click the "Details" summary/button found
via within(dialog).getByText('Details') or getByRole) and finally assert the raw
error text is visible; reference the AIPanel render and the
ProviderSetupErrorMessage/Details control used in the dialog when updating the
assertions.
🤖 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 `@app/src/components/settings/panels/__tests__/AIPanel.test.tsx`:
- Around line 344-366: Update the test in AIPanel.test.tsx to explicitly verify
the expand/collapse behavior: after confirming the friendly alert text, assert
the raw error text is not visible (e.g., query the <details> disclosure or use a
visibility assertion on the raw string
within(dialog).queryByText(/invalid_api_key_invalid_api_key/)). Then simulate
the user expanding the Details control (click the "Details" summary/button found
via within(dialog).getByText('Details') or getByRole) and finally assert the raw
error text is visible; reference the AIPanel render and the
ProviderSetupErrorMessage/Details control used in the dialog when updating the
assertions.

In `@app/src/components/settings/panels/AIPanel.tsx`:
- Around line 205-225: formatProviderSetupError currently uses a hardcoded
English fallback string; update it to use i18n by either (A) adding a
translation key (e.g. "provider.setup_failed") to your i18n resources and
changing formatProviderSetupError to accept a t function parameter (e.g.
formatProviderSetupError(err: unknown, t: TFunction)) and call
t('provider.setup_failed') instead of the literal, or (B) keep
formatProviderSetupError pure and remove the fallback here, returning
undefined/detail only and let callers (components using useT()) supply the
localized fallback; ensure ProviderSetupError consumers are updated accordingly
to use t() or useT() and add the new translation key to the locale files.
- Around line 673-691: ProviderSetupErrorMessage currently hardcodes the
"Details" summary text; replace that literal with a call to the i18n function
(t) to match the rest of the component. In the ProviderSetupErrorMessage
component, change the <summary> content from "Details" to t('<appropriate.key>')
(e.g., t('settings.ai.details') or whatever existing localization key you
prefer), import/use the same t function/context as the parent component so the
label is internationalized consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b5a30f5c-cc4f-4577-926b-52ccced84f1d

📥 Commits

Reviewing files that changed from the base of the PR and between 6281aea and 2c3bbd2.

📒 Files selected for processing (2)
  • app/src/components/settings/panels/AIPanel.tsx
  • app/src/components/settings/panels/__tests__/AIPanel.test.tsx

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 21, 2026
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: 1

🧹 Nitpick comments (1)
app/src/components/settings/panels/__tests__/AIPanel.test.tsx (1)

344-366: ⚡ Quick win

Strengthen this test to verify disclosure behavior, not just DOM presence.

This currently checks detail text exists without expanding “Details”, so it doesn’t prove the raw payload is hidden until user action.

✅ Suggested assertion shape
   const alert = await within(dialog).findByRole('alert');
   expect(alert).toHaveTextContent(/Could not reach OpenAI\./i);
   expect(alert).not.toHaveTextContent('invalid_api_key');
-  expect(within(dialog).getByText('Details')).toBeInTheDocument();
-  expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeInTheDocument();
+  const detailsSummary = within(dialog).getByText('Details');
+  const details = detailsSummary.closest('details') as HTMLDetailsElement;
+  expect(details.open).toBe(false);
+  fireEvent.click(detailsSummary);
+  expect(details.open).toBe(true);
+  expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeInTheDocument();
🤖 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 `@app/src/components/settings/panels/__tests__/AIPanel.test.tsx` around lines
344 - 366, The test currently only checks that the details text exists in the
DOM but doesn't verify that it's initially hidden and only revealed after user
action; update the 'keeps long provider setup errors contained with raw detail
behind details' test to assert that the raw payload string (e.g.,
'invalid_api_key_invalid_api_key') is not visible inside the alert element
initially (use within(dialog).findByRole('alert') and
expect(...).not.toHaveTextContent(...)), then simulate expanding the Details
control (fireEvent.click(on the element returned by
within(dialog).getByText('Details') or getByRole if available) and assert
afterwards that the raw payload is visible
(expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeInTheDocument()).
Ensure you still keep the existing checks for the generic error message and that
Details exists.
🤖 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 `@app/src/components/settings/panels/AIPanel.tsx`:
- Around line 223-224: The current code sets detail to the full raw provider
payload which can bloat React state; modify the assignment around const detail =
raw.trim() !== summary ? raw.trim() : undefined to enforce a maximum length
(e.g., const MAX_DETAIL_LENGTH = 1000) and if raw.trim() exceeds that limit,
truncate it (append an ellipsis) before setting detail, while preserving the
existing equality check with summary so detail remains undefined when identical
to summary; update any references that expect full raw to use the truncated
value or expose the full payload only on demand (e.g., via "view full error"
action).

---

Nitpick comments:
In `@app/src/components/settings/panels/__tests__/AIPanel.test.tsx`:
- Around line 344-366: The test currently only checks that the details text
exists in the DOM but doesn't verify that it's initially hidden and only
revealed after user action; update the 'keeps long provider setup errors
contained with raw detail behind details' test to assert that the raw payload
string (e.g., 'invalid_api_key_invalid_api_key') is not visible inside the alert
element initially (use within(dialog).findByRole('alert') and
expect(...).not.toHaveTextContent(...)), then simulate expanding the Details
control (fireEvent.click(on the element returned by
within(dialog).getByText('Details') or getByRole if available) and assert
afterwards that the raw payload is visible
(expect(within(dialog).getByText(/invalid_api_key_invalid_api_key/)).toBeInTheDocument()).
Ensure you still keep the existing checks for the generic error message and that
Details exists.
🪄 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: 36ad4671-621f-499f-9353-44fd38a2ace1

📥 Commits

Reviewing files that changed from the base of the PR and between 2c3bbd2 and 03194b2.

📒 Files selected for processing (2)
  • app/src/components/settings/panels/AIPanel.tsx
  • app/src/components/settings/panels/__tests__/AIPanel.test.tsx

Comment thread app/src/components/settings/panels/AIPanel.tsx Outdated
@aqilaziz aqilaziz force-pushed the codex/2363-ai-provider-error-wrap branch from 03194b2 to 266a570 Compare May 21, 2026 05:32
@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 21, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 21, 2026
@senamakel senamakel self-assigned this May 25, 2026
senamakel added 2 commits May 24, 2026 21:36
# Conflicts:
#	app/src/components/settings/panels/AIPanel.tsx
#	app/src/components/settings/panels/__tests__/AIPanel.test.tsx
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 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.

Provider setup error text overflows the modal

2 participants