{"record":{"id":"cc6bbf450e367304","repo":"yikart/AiToEarn","slug":"http-response-status-errortext","errorCode":null,"errorMessage":"HTTP ${response.status}: ${errorText}","messagePattern":"HTTP (.+?): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-web/src/api/ai/ai.api.ts","lineNumber":131,"sourceCode":"          'Authorization': `Bearer ${useUserStore.getState().token || ''}`,\n          'Accept-Language': lng,\n        },\n        body: JSON.stringify(params),\n        signal: abortController.signal,\n        openWhenHidden: true,\n\n        // 当连接打开时\n        async onopen(response) {\n          if (response.ok) {\n            return // 一切正常，继续处理消息\n          }\n\n          // 处理错误响应\n          if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n            // 客户端错误，不重试\n            const errorText = await response.text()\n            console.error('[SSE] Client error:', response.status, errorText)\n            throw new Error(`HTTP ${response.status}: ${errorText}`)\n          }\n          else {\n            // 服务器错误或其他问题，不自动重试，直接抛出错误\n            console.error('[SSE] Server error:', response.status)\n            throw new Error(`HTTP ${response.status}`)\n          }\n        },\n\n        // 当收到消息时\n        onmessage(event) {\n          // 如果已完成，忽略后续消息\n          if (isCompleted) {\n            return\n          }\n\n          // 如果没有数据，跳过\n          if (!event.data) {\n            return","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-web/src/api/ai/ai.api.ts#L113-L149","documentation":" thrown by the SSE onopen handler in ai.api.ts when the fetch-based EventSource receives an HTTP 4xx status (except 429, which is treated as retryable). It reads the response body as errorText and embeds it in the message so the server's error reason is preserved. Client errors are deliberately not retried.","triggerScenarios":"Opening an AI chat SSE stream when the server returns 400/401/403/404 etc., e.g. invalid API key, malformed request body, expired token, or nonexistent endpoint.","commonSituations":"Expired or missing auth token in the SSE request; backend rejecting the chat payload (bad model name); proxy/gateway (nginx) returning 404 for the stream path; API version mismatch after deployment.","solutions":["Read the errorText after 'HTTP <status>: ' to see the server's actual rejection reason.","Refresh the auth token / re-login if status is 401 or 403.","Validate the request payload (model, conversationId, messages) against the current backend API.","Check proxy/gateway routing config if status is 404 and other endpoints work.","Add client-side handling to surface this message to the UI instead of an unhandled rejection."],"exampleFix":"// before\nonopen(response) { /* no auth pre-check */ }\n// after\nif (isTokenExpired()) await refreshToken()\nawait openSSEStream(...)","handlingStrategy":"try-catch","validationCode":"// before opening the stream\nif (!token) throw new Error('未登录，无法建立 AI 连接')\nif (!navigator.onLine) throw new Error('网络不可用')","typeGuard":"function isClientHttpError(e: unknown): e is Error & { status: number } {\n  const m = e instanceof Error && /^HTTP (4\\d\\d):/.exec(e.message)\n  return !!m && e instanceof Error\n}","tryCatchPattern":"try {\n  await openAiSseStream(params)\n}\ncatch (e) {\n  const m = /^HTTP (\\d{3}):/.exec(e instanceof Error ? e.message : '')\n  const status = m ? Number(m[1]) : 0\n  if (status === 401 || status === 403) await reloginAndRetry()\n  else if (status === 0 || status >= 500) queueRetry()\n  else showError(e)\n}","preventionTips":["Refresh auth tokens proactively before opening long-lived SSE connections.","Validate the stream request payload against the current API contract.","Monitor 4xx rates on the SSE endpoint to catch contract/auth drift early.","Surface the errorText portion of the message to users instead of a generic message."],"tags":["sse","http-client-error","streaming","network"],"backgroundTag":"sse-connection-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}