{"record":{"id":"a6d6a81abfa63f27","repo":"can1357/oh-my-pi","slug":"anthropic-api-error-response-status-errort","errorCode":null,"errorMessage":"Anthropic API error (${response.status}): ${errorText}","messagePattern":"Anthropic API error \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"SearchProviderError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/anthropic.ts","lineNumber":220,"sourceCode":"\tif (systemBlocks && systemBlocks.length > 0) {\n\t\tbody.system = systemBlocks;\n\t}\n\n\t// OAuth requests inject the CC billing header (buildSystemBlocks); patch its\n\t// cch attestation like the streaming path instead of shipping `cch=00000`.\n\tconst doFetch = auth.isOAuth ? wrapFetchForCch(fetchImpl) : fetchImpl;\n\tconst response = await doFetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders,\n\t\tbody: JSON.stringify(body),\n\t\tsignal: withHardTimeout(signal, timeoutMs),\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst classified = classifyProviderHttpError(\"anthropic\", response.status, errorText);\n\t\tif (classified) throw classified;\n\t\tthrow new SearchProviderError(\n\t\t\t\"anthropic\",\n\t\t\t`Anthropic API error (${response.status}): ${errorText}`,\n\t\t\tresponse.status,\n\t\t);\n\t}\n\n\treturn response.json() as Promise<AnthropicApiResponse>;\n}\n\n/**\n * Parses a human-readable page age string into seconds.\n * @param pageAge - Age string like \"2 days ago\", \"3h ago\", \"1 week ago\"\n * @returns Age in seconds, or undefined if parsing fails\n */\nfunction parsePageAge(pageAge: string | null | undefined): number | undefined {\n\tif (!pageAge) return undefined;\n\n\tconst match = pageAge.match(/^(\\d+)\\s*(s|sec|second|m|min|minute|h|hour|d|day|w|week|mo|month|y|year)s?\\s*(ago)?$/i);","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/anthropic.ts#L202-L238","documentation":"The Anthropic search provider calls the Anthropic API; on a non-OK HTTP status it first tries classifyProviderHttpError to map known statuses to richer errors. If unclassified, it throws SearchProviderError carrying the raw status and response body text — the generic fallback for any API rejection (auth, rate limit, server error, malformed request).","triggerScenarios":"Anthropic API returns 401/403 (bad key), 429 (rate limit), 5xx (outage), or 400 (bad request) and the error classifier doesn't produce a specialized error.","commonSituations":"Expired or revoked ANTHROPIC_API_KEY; account out of credits; org blocked for web search; Anthropic incident/outage; request exceeding rate limits under parallel tool calls.","solutions":["Read the embedded status and errorText to identify root cause (401 → fix key, 429 → back off, 5xx → retry later).","Verify/rotate the Anthropic API key in auth storage.","Catch SearchProviderError and fall back to another search provider in the chain.","Add retry with exponential backoff for 429/5xx statuses before surfacing the failure."],"exampleFix":"// before\nconst res = await runSearchQuery({ query, provider: \"anthropic\" }, opts);\n// after\ntry {\n  return await runSearchQuery({ query, provider: \"anthropic\" }, opts);\n} catch (e) {\n  if (e instanceof SearchProviderError && (e.status === 429 || (e.status ?? 500) >= 500)) {\n    await Bun.sleep(2000);\n    return await runSearchQuery({ query }, opts); // retry / fallback chain\n  }\n  throw e;\n}","handlingStrategy":"fallback","validationCode":"// Pre-flight: verify credentials exist before making the call\nconst provider = await getSearchProvider(\"anthropic\");\nif (!(await provider.isAvailable(authStorage))) {\n  throw new Error(\"Anthropic credentials missing/invalid — configure before searching\");\n}","typeGuard":"null","tryCatchPattern":"try { return await searchWith(\"anthropic\", params); }\ncatch (e) {\n  if (e instanceof SearchProviderError && typeof e.status === \"number\") {\n    if (e.status === 429 || e.status >= 500) return await searchWithAutomatic(params); // retryable\n    if (e.status === 401 || e.status === 403) throw new Error(\"Fix Anthropic credentials\");\n  }\n  throw e;\n}","preventionTips":["Rotate and verify API keys regularly; surface 401/403 immediately to the user.","Back off on 429/5xx with exponential retry before failing.","Use the automatic provider chain so Anthropic outages degrade gracefully.","Keep classifyProviderHttpError mappings current for richer, actionable errors."],"tags":["api-error","http","anthropic","search-provider"],"backgroundTag":"api-http-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}