{"record":{"id":"35330a6f74b03bb2","repo":"linshenkx/prompt-optimizer","slug":"unexpected-api-response-format-35330a","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/openai-adapter.ts","lineNumber":165,"sourceCode":"      const response = await openai.models.list()\n\n      // 检查返回格式\n      if (response && response.data && Array.isArray(response.data)) {\n        const models = response.data\n          .map((model) => {\n            // 使用buildDefaultModel为每个模型ID创建TextModel对象\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        return models\n      }\n\n      throw new APIError('Unexpected API response format')\n    } catch (error: any) {\n      console.error('[OpenAIAdapter] Failed to fetch models:', error)\n\n      // 连接错误处理（包括跨域检测）\n      if (error.message && (error.message.includes('Failed to fetch') ||\n          error.message.includes('Connection error'))) {\n        const isCrossOriginError = this.detectCrossOriginError(error, baseURL)\n\n        if (isCrossOriginError) {\n          throw new APIError(`Cross-origin connection failed: ${error.message}`)\n        } else {\n          throw new APIError(`Connection failed: ${error.message}`)\n        }\n      }\n\n      // API返回的错误信息\n      if (error.response?.data) {\n        throw new APIError(`API error: ${JSON.stringify(error.response.data)}`)","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/llm/adapters/openai-adapter.ts#L147-L183","documentation":"The response from the OpenAI-compatible /v1/models endpoint did not contain the expected data array (the if-branch that maps models was skipped), so OpenAIAdapter throws the generic 'Unexpected API response format' APIError.","triggerScenarios":"Base URL points at a server returning JSON without data (e.g. an error object with 200), an HTML page, or a differently-versioned models endpoint; response.json() parsed fine but the shape check failed.","commonSituations":"Wrong baseURL (pointing at a dashboard URL instead of the API root), Azure-style endpoints needing a query param, proxies returning wrapped responses.","solutions":["Verify baseURL ends at the API root such that {baseURL}/models resolves (adapter appends the path)","curl {baseURL}/models with the key and inspect the JSON — expect { data: [...] }","If the server uses a different path, set baseURL so the final URL is correct","Upgrade the adapter/gateway if the server's models schema diverged"],"exampleFix":"// before\nbaseURL: 'https://my-gw.example.com'\n\n// after\nbaseURL: 'https://my-gw.example.com/v1' // so GET {baseURL}/models returns { data: [...] }","handlingStrategy":"validation","validationCode":"const r = await fetch(`${baseURL.replace(/\\/$/, '')}/models`, { headers: { Authorization: `Bearer ${key}` } })\nconst body = await r.json()\nif (!Array.isArray(body?.data)) throw new Error(`${baseURL} is not a valid OpenAI-compatible models endpoint`)","typeGuard":"function isOpenAIModelsResponse(d: unknown): d is { data: { id: string }[] } {\n  return Array.isArray((d as any)?.data) && (d as any).data.every((m: any) => typeof m?.id === 'string')\n}","tryCatchPattern":"null","preventionTips":["Set baseURL to the API root (usually ends with /v1)","Verify endpoints with curl before configuring","Prefer official provider base URLs over guessed ones"],"tags":["openai","response-format","base-url","model-list"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}