feat(ai-chat): surface the real cause in the error banner

The AI chat error banner always showed a generic "Something went wrong"
with no reason. The server already forwards the provider cause into the
stream (e.g. "Cannot connect to API: read ECONNRESET"), but the client
hid it behind a static heading.

- describeChatError now returns { title, detail }: a short heading naming
  the cause category plus a one-line explanation.
- Add classifyProviderError: maps connection reset, timeout, rate limit,
  context-window overflow, quota and auth failures to clear categories;
  the 403/503 gating responses are preserved; unknown errors fall back to
  the verbatim provider text.
- Match HTTP status codes only as the leading token and textual signatures
  only against the message head (before "| response body:"), so a number
  or phrase in the response-body snippet never mislabels the cause.
- Use the new {title, detail} in all three banners: chat-thread,
  share-ai-widget and the persisted-error banner in message-item.
- Cover the classifier with 20 unit tests (categories + regressions).
This commit is contained in:
claude_code
2026-06-21 18:54:43 +03:00
parent 0cfc3c8f89
commit 0bbf94c154
5 changed files with 310 additions and 48 deletions

View File

@@ -88,6 +88,10 @@ export default function ShareAiWidget({
const isStreaming = status === "submitted" || status === "streaming";
// Same classified-error banner as the internal chat: name the cause instead of a
// generic heading.
const errorView = error ? describeChatError(error.message ?? "", t) : null;
const handleSend = () => {
const text = input.trim();
if (!text || isStreaming) return;
@@ -173,18 +177,18 @@ export default function ShareAiWidget({
/>
</Box>
{error && (
{errorView && (
<Alert
variant="light"
color="red"
icon={<IconAlertTriangle size={16} />}
mx="sm"
mb="xs"
title={t("Something went wrong")}
title={errorView.title}
>
{/* Surface the real cause (provider/gating message) instead of a
{/* Surface the real cause (provider/gating category) instead of a
generic line — same helper the internal chat uses. */}
{describeChatError(error.message ?? "", t)}
{errorView.detail}
</Alert>
)}