Skip to content

fix: improve AI provider connection test error handling#407

Merged
datlechin merged 2 commits intomainfrom
fix/ai-provider-error-handling
Mar 21, 2026
Merged

fix: improve AI provider connection test error handling#407
datlechin merged 2 commits intomainfrom
fix/ai-provider-error-handling

Conversation

@datlechin
Copy link
Collaborator

@datlechin datlechin commented Mar 21, 2026

Summary

  • Anthropic: fetch models dynamically from /v1/models API with hardcoded fallback; test connection now treats 400 (billing) as success since the key is valid; parse JSON error responses for clean messages; 401 shows "Authentication failed. Check your API key." instead of raw API text
  • OpenAI/Gemini: throw descriptive errors on auth failure instead of silent return false; parse JSON error responses; trim API keys
  • Settings UI: disable Test button when API key field is empty; guard against empty key before making test request

Test plan

  • Configure Anthropic provider with valid key → Test shows "Connection successful"
  • Configure Anthropic provider with invalid key → Test shows "Authentication failed. Check your API key."
  • Configure Anthropic provider with no key → Test button is disabled
  • Configure Anthropic provider with valid key but no credits → Test shows "Connection successful"
  • Verify Anthropic model list fetches dynamically from API
  • Configure OpenAI provider with invalid key → Test shows auth error
  • Configure Gemini provider with invalid key → Test shows auth error

Summary by CodeRabbit

  • Bug Fixes

    • API keys are now trimmed of leading/trailing whitespace before use.
    • Connection tests validate required API keys and return clearer success/failure results.
  • Improvements

    • Error responses from AI services now surface parsed, user-friendly messages.
    • Improved handling of response size limits for error reporting.
  • New Features

    • Anthropic provider fetches available models from the API with a maintained fallback list.

@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

API keys are trimmed on init across providers; providers now parse JSON error messages and map HTTP errors accordingly. AnthropicProvider fetches models from the API with a local fallback and adjusted testConnection behavior. Settings UI prevents testing when required API keys are blank.

Changes

Cohort / File(s) Summary
AI provider core changes
TablePro/Core/AI/AnthropicProvider.swift, TablePro/Core/AI/GeminiProvider.swift, TablePro/Core/AI/OpenAICompatibleProvider.swift, TablePro/Core/AI/AIProvider.swift
Trim API keys at initialization. Add AIProviderError.parseErrorMessage(from:) and use parsed messages when mapping HTTP errors. Standardize testConnection error handling (401 -> authenticationFailed). Adjust SSE/error-body truncation to use NSString length. AnthropicProvider: fetchAvailableModels() now calls {endpoint}/v1/models and falls back to a knownModels list; testConnection uses a different default model and treats 200/400 as success with 401 throwing authenticationFailed.
Settings validation
TablePro/Views/Settings/AISettingsView.swift
Disable Test button when provider requires an API key and the provided key is empty/whitespace. testProvider() early-returns with a "API key is required" failure when the key is missing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I trimmed my keys beneath the moon,

fetched models humbly, none too soon,
parsed the errors so they’d speak,
tested only when keys aren’t weak,
a hop, a patch, a tidy tune.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main focus of the pull request: improving error handling for AI provider connection tests across multiple providers.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ai-provider-error-handling

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

Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
TablePro/Core/AI/OpenAICompatibleProvider.swift (1)

149-158: ⚠️ Potential issue | 🟡 Minor

Inconsistent error handling for non-200 responses.

The test only throws for HTTP 401, but other error codes (403, 429, 500, etc.) will still return true if the response is JSON. This differs from GeminiProvider.testConnection() which throws for all non-200 responses. Consider aligning the behavior:

🔧 Proposed fix to handle all HTTP errors
             if httpResponse.statusCode == 401 {
                 throw AIProviderError.authenticationFailed("")
             }

+            // Throw mapped error for other non-success status codes
+            if httpResponse.statusCode >= 400 {
+                let body = String(data: data, encoding: .utf8) ?? ""
+                throw mapHTTPError(statusCode: httpResponse.statusCode, body: body)
+            }
+
             // Non-JSON response means wrong endpoint (e.g., HTML 404 page)
             if !isJSON {
                 return false
             }

Note: This would require capturing data from session.data(for: request) at Line 139.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Core/AI/OpenAICompatibleProvider.swift` around lines 149 - 158, The
testConnection implementation in OpenAICompatibleProvider currently only throws
on 401 and returns true for other JSON non-200 responses; align it with
GeminiProvider.testConnection by treating any non-2xx HTTP status as an error:
capture the response body data from session.data(for: request) (as noted at the
earlier call site), check if httpResponse.statusCode is in 200..<300 and if not,
throw an appropriate AIProviderError (use
AIProviderError.authenticationFailed("") for 401 and a generic HTTP error for
other codes, including any message parsed from the captured data/JSON) instead
of returning true; reference the testConnection method and AIProviderError types
to locate where to change the status code check and error construction.
🧹 Nitpick comments (2)
TablePro/Views/Settings/AISettingsView.swift (1)

549-553: Consider simplifying the guard condition for readability.

The double-negative logic is hard to parse. Consider restructuring for clarity:

♻️ Proposed refactor
     func testProvider() {
-        guard !editingAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || !draft.type.requiresAPIKey else {
+        let trimmedKey = editingAPIKey.trimmingCharacters(in: .whitespacesAndNewlines)
+        if draft.type.requiresAPIKey && trimmedKey.isEmpty {
             testResult = .failure(String(localized: "API key is required"))
             return
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Views/Settings/AISettingsView.swift` around lines 549 - 553, The
guard in testProvider uses a double-negative which is hard to read; replace it
with a straightforward conditional that first checks if
draft.type.requiresAPIKey is true and then checks whether
editingAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, setting
testResult = .failure(...) and returning in that case, otherwise continue —
reference the testProvider function and the editingAPIKey,
draft.type.requiresAPIKey and testResult symbols when making the change.
TablePro/Core/AI/GeminiProvider.swift (1)

224-233: Consider extracting shared error parsing logic.

This parseErrorMessage implementation is identical across GeminiProvider, OpenAICompatibleProvider, and AnthropicProvider. Could be extracted to a shared utility or protocol extension to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Core/AI/GeminiProvider.swift` around lines 224 - 233, The
parseErrorMessage implementation is duplicated in GeminiProvider,
OpenAICompatibleProvider, and AnthropicProvider; extract it into a single shared
implementation (either a utility function or a protocol extension) and have each
provider call that shared routine instead of keeping local copies. For example,
create a common helper (e.g., an ErrorParsing utility function or an
ErrorParsable protocol with a default parseErrorMessage(_:)->String? extension)
and replace the parseErrorMessage implementations in GeminiProvider,
OpenAICompatibleProvider, and AnthropicProvider with calls to that shared helper
to remove duplication and centralize parsing logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@TablePro/Core/AI/OpenAICompatibleProvider.swift`:
- Around line 149-158: The testConnection implementation in
OpenAICompatibleProvider currently only throws on 401 and returns true for other
JSON non-200 responses; align it with GeminiProvider.testConnection by treating
any non-2xx HTTP status as an error: capture the response body data from
session.data(for: request) (as noted at the earlier call site), check if
httpResponse.statusCode is in 200..<300 and if not, throw an appropriate
AIProviderError (use AIProviderError.authenticationFailed("") for 401 and a
generic HTTP error for other codes, including any message parsed from the
captured data/JSON) instead of returning true; reference the testConnection
method and AIProviderError types to locate where to change the status code check
and error construction.

---

Nitpick comments:
In `@TablePro/Core/AI/GeminiProvider.swift`:
- Around line 224-233: The parseErrorMessage implementation is duplicated in
GeminiProvider, OpenAICompatibleProvider, and AnthropicProvider; extract it into
a single shared implementation (either a utility function or a protocol
extension) and have each provider call that shared routine instead of keeping
local copies. For example, create a common helper (e.g., an ErrorParsing utility
function or an ErrorParsable protocol with a default
parseErrorMessage(_:)->String? extension) and replace the parseErrorMessage
implementations in GeminiProvider, OpenAICompatibleProvider, and
AnthropicProvider with calls to that shared helper to remove duplication and
centralize parsing logic.

In `@TablePro/Views/Settings/AISettingsView.swift`:
- Around line 549-553: The guard in testProvider uses a double-negative which is
hard to read; replace it with a straightforward conditional that first checks if
draft.type.requiresAPIKey is true and then checks whether
editingAPIKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, setting
testResult = .failure(...) and returning in that case, otherwise continue —
reference the testProvider function and the editingAPIKey,
draft.type.requiresAPIKey and testResult symbols when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7ecd2f6b-7772-458c-9aa6-7937e14cb500

📥 Commits

Reviewing files that changed from the base of the PR and between 915d75d and 07619cb.

📒 Files selected for processing (4)
  • TablePro/Core/AI/AnthropicProvider.swift
  • TablePro/Core/AI/GeminiProvider.swift
  • TablePro/Core/AI/OpenAICompatibleProvider.swift
  • TablePro/Views/Settings/AISettingsView.swift

@datlechin datlechin merged commit 28ce250 into main Mar 21, 2026
2 of 3 checks passed
@datlechin datlechin deleted the fix/ai-provider-error-handling branch March 21, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant