{"record":{"id":"11a2adc097506b44","repo":"koala73/worldmonitor","slug":"response-status-429-rate-limited-prov","errorCode":null,"errorMessage":"response.status === 429 ? 'Rate limited' : `${provider} API error`","messagePattern":"response\\.status === 429 \\? 'Rate limited' : `(.+?) API error`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/worldmonitor/news/v1/summarize-article.ts","lineNumber":301,"sourceCode":"            model,\n            messages: [\n              { role: 'system', content: effectiveSystemPrompt },\n              { role: 'user', content: userPrompt },\n            ],\n            temperature: 0.3,\n            max_tokens: 100,\n            top_p: 0.9,\n            ...extraBody,\n          }),\n          signal: AbortSignal.timeout(25_000),\n        });\n\n        if (!response.ok) {\n          const errorText = await response.text();\n          console.error(`[SummarizeArticle:${provider}] API error:`, response.status, errorText);\n          recordModelFailure(apiUrl, model, response.status, errorText);\n          await emitSummarizeLlmEvent({ provider, model, ok: false, durationMs: Date.now() - llmStartMs, promptChars: llmPromptChars, reason: `http_${response.status}` });\n          throw new Error(response.status === 429 ? 'Rate limited' : `${provider} API error`);\n        }\n\n        // HTTP success proves provider/model compatibility. Summary validation\n        // below is an application-level concern and must not preserve a stale\n        // model-rejection streak.\n        recordModelSuccess(apiUrl, model);\n\n        const data = await response.json() as any;\n        const tokens = (data.usage?.total_tokens as number) || 0;\n        const usage = data.usage as { total_tokens?: number; prompt_tokens?: number; completion_tokens?: number } | undefined;\n        const message = data.choices?.[0]?.message;\n        const rawText = typeof message?.content === 'string' ? message.content.trim() : '';\n        const rawContent = stripThinkingTags(rawText);\n\n        if (['brief', 'analysis'].includes(mode) && rawContent.length < 20) {\n          console.warn(`[SummarizeArticle:${provider}] Output too short after stripping (${rawContent.length} chars), rejecting`);\n          await emitSummarizeLlmEvent({ provider, model, ok: false, durationMs: Date.now() - llmStartMs, promptChars: llmPromptChars, usage, reason: 'stripped_empty' });\n          return null;","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/koala73/worldmonitor/blob/a96956387a927b8cd7aa34b0c41fca357e746be9/server/worldmonitor/news/v1/summarize-article.ts#L283-L319","documentation":"Server error in summarizeArticle: the LLM provider HTTP call for article summarization returned a non-OK status. 429 is reported as 'Rate limited'; other statuses as a generic provider API error. The status and body are logged and model failure tracking is recorded before this surfaces.","triggerScenarios":"Thrown at server/worldmonitor/news/v1/summarize-article.ts:301 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["For 429, back off and retry — provider rate limits are the usual cause","For other statuses, check the logged response body for provider-side errors (auth, quota, malformed request)","Retry later; transient provider failures often resolve on their own"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a96956387a927b8cd7aa34b0c41fca357e746be9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}