{"record":{"id":"6a758b4f51c29be1","repo":"linshenkx/prompt-optimizer","slug":"unexpected-api-response-format","errorCode":null,"errorMessage":"Unexpected API response format","messagePattern":"Unexpected API response format","errorType":"exception","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/llm/adapters/anthropic-adapter.ts","lineNumber":138,"sourceCode":"      // 检查返回格式\n      if (response && response.data && Array.isArray(response.data)) {\n        const models = response.data\n          .map((model: any) => {\n            // 使用 buildDefaultModel 为每个模型 ID 创建 TextModel 对象\n            // Anthropic API 返回的 model 对象包含: id, name, version, capabilities\n            return this.buildDefaultModel(model.id)\n          })\n          .sort((a, b) => a.id.localeCompare(b.id))\n\n        if (models.length === 0) {\n          throw new APIError('API returned empty model list')\n        }\n\n        console.log(`[AnthropicAdapter] Successfully fetched ${models.length} models`)\n        return models\n      }\n\n      throw new APIError('Unexpected API response format')\n    } catch (error: any) {\n      console.error('[AnthropicAdapter] Failed to fetch models:', error)\n\n      // 连接错误处理（包括跨域检测）\n      if (error.message && (error.message.includes('Failed to fetch') ||\n          error.message.includes('NetworkError') ||\n          error.message.includes('ECONNREFUSED') ||\n          error.message.includes('CORS'))) {\n        throw new APIError(`Network error: ${error.message}`)\n      }\n\n      // API 错误处理\n      if (error.status) {\n        throw new APIError(`Anthropic API error (${error.status}): ${error.message}`)\n      }\n\n      // 其他错误\n      throw error","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/llm/adapters/anthropic-adapter.ts#L120-L156","documentation":"Thrown by AnthropicAdapter.getModelsAsync when the response body from the /v1/models endpoint does not match the expected shape (a JSON object with a data array). The adapter successfully got an HTTP response but could not parse any models out of it, so it aborts with a generic format error instead of returning garbage.","triggerScenarios":"Calling getModelsAsync() against an endpoint that returns non-JSON or a different schema: a proxy/gateway HTML error page with 200 status, a base URL pointing at an OpenAI-compatible server rather than Anthropic's API, or an API version that changed the models payload.","commonSituations":"Misconfigured baseURL (e.g. https://api.openai.com with an Anthropic key), self-hosted gateways (one-api, litellm) that don't implement GET /v1/models the Anthropic way, corporate proxies returning HTML login pages.","solutions":["Verify the base URL is the real Anthropic API (https://api.anthropic.com) or a gateway that faithfully implements GET /v1/models","curl the endpoint manually: curl -H \"x-api-key: $KEY\" $BASE_URL/v1/models and inspect the JSON shape","If using a proxy, fix or upgrade it so { data: [...] } is returned","Handle the APIError in the caller and surface a fallback static model list"],"exampleFix":"// before\nconst models = await adapter.getModelsAsync()\n\n// after\nlet models\ntry {\n  models = await adapter.getModelsAsync()\n} catch (e) {\n  if (e instanceof APIError && /Unexpected API response format/.test(e.message)) {\n    models = FALLBACK_ANTHROPIC_MODELS\n  } else throw e\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"function isAnthropicModelsPayload(d: unknown): d is { data: unknown[] } {\n  return !!d && typeof d === 'object' && Array.isArray((d as any).data)\n}","tryCatchPattern":"try {\n  const models = await adapter.getModelsAsync()\n} catch (e) {\n  if (e instanceof APIError && e.message === 'Unexpected API response format') {\n    // verify endpoint manually, fall back to static model list\n  } else throw e\n}","preventionTips":["Only point baseURL at servers that implement Anthropic's /v1/models schema","Smoke-test gateways with curl before wiring them into config","Keep a hardcoded fallback model list for major providers"],"tags":["anthropic","model-list","response-format","api"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}