{"record":{"id":"56c63833599df643","repo":"ruvnet/ruflo","slug":"openai-api-error-response-status-error","errorCode":null,"errorMessage":"OpenAI API error: ${response.status} - ${error}","messagePattern":"OpenAI API error: (.+?) - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/embeddings/src/embedding-service.ts","lineNumber":348,"sourceCode":"        const response = await fetch(this.baseURL, {\n          method: 'POST',\n          headers: {\n            'Content-Type': 'application/json',\n            Authorization: `Bearer ${this.apiKey}`,\n          },\n          body: JSON.stringify({\n            model: this.model,\n            input: texts,\n            dimensions: config.dimensions,\n          }),\n          signal: controller.signal,\n        });\n\n        clearTimeout(timeoutId);\n\n        if (!response.ok) {\n          const error = await response.text();\n          throw new Error(`OpenAI API error: ${response.status} - ${error}`);\n        }\n\n        return await response.json() as {\n          data: Array<{ embedding: number[] }>;\n          usage?: { prompt_tokens: number; total_tokens: number };\n        };\n      } catch (error) {\n        if (attempt === this.maxRetries - 1) {\n          throw error;\n        }\n        // Exponential backoff\n        await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 100));\n      }\n    }\n\n    throw new Error('Max retries exceeded');\n  }\n}","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/embeddings/src/embedding-service.ts#L330-L366","documentation":"Inside callOpenAI(), a non-2xx response from POST to config.baseURL is read as text and thrown as 'OpenAI API error: <status> - <body>'. The surrounding retry loop re-attempts with exponential backoff (2^attempt * 100ms) and rethrows on the final attempt (maxRetries, default 3) — so whatever status surfaces has already been retried, including non-retryable 4xx.","triggerScenarios":"401 invalid apiKey; 429 rate limit or quota exhausted; 400 invalid request (e.g. dimensions unsupported by the chosen model); 404 wrong baseURL path or model name; embedBatch() sending many uncached texts in one payload hitting size/rate limits.","commonSituations":"Missing/expired OpenAI key; bursty embedBatch calls hitting org rate limits; using the dimensions option with a model that does not support it; pointing baseURL at an Azure or proxy endpoint with a different path shape.","solutions":["Map the status: 401 → fix config.apiKey; 429 → batch smaller, throttle, or raise config.maxRetries; 400 → check model/dimensions compatibility; 404 → verify config.baseURL and model name","For 429s, add client-side throttling between embedBatch calls rather than relying on the built-in 3 retries","Inspect the body text in the message — the provider error JSON names the offending parameter (e.g. invalid_request_error with the field)"],"exampleFix":"// before\nconst svc = new OpenAIEmbeddingService({ apiKey, model: 'text-embedding-ada-003' });\nawait svc.embedBatch(texts); // OpenAI API error: 404 - model not found\n\n// after\nconst svc = new OpenAIEmbeddingService({ apiKey, model: 'text-embedding-3-small' });\nawait svc.embedBatch(texts);","handlingStrategy":"retry","validationCode":"function classifyOpenAiStatus(message: string): 'auth' | 'rate' | 'request' | 'notfound' | 'unknown' {\n  const m = message.match(/OpenAI API error: (\\d{3})/);\n  if (!m) return 'unknown';\n  return { 401: 'auth', 403: 'auth', 429: 'rate', 400: 'request', 404: 'notfound' }[m[1]] ?? 'unknown';\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 5; attempt++) {\n  try {\n    return await svc.embedBatch(texts);\n  } catch (e) {\n    const kind = classifyOpenAiStatus(e instanceof Error ? e.message : '');\n    if (kind === 'auth' || kind === 'request') throw e;        // do not retry client errors\n    if (attempt === 4) throw e;                                  // retries already done in-library\n    await new Promise(r => setTimeout(r, 2 ** attempt * 500));   // extra backoff for 429/5xx\n  }\n}","preventionTips":["The library already retries 3 times — add caller-side throttling for 429 instead of tight retry loops","Log the response body from the message; it names the exact invalid parameter for 400s"],"tags":["openai","embeddings","http","rate-limit","api"],"backgroundTag":"openai-api-http-error","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}