ruvnet/ruflo · error
OpenAI API error: ${response.status} - ${error}
Error message
OpenAI API error: ${response.status} - ${error} What it means
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.
Source
Thrown at v3/@claude-flow/embeddings/src/embedding-service.ts:348
const response = await fetch(this.baseURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
input: texts,
dimensions: config.dimensions,
}),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const error = await response.text();
throw new Error(`OpenAI API error: ${response.status} - ${error}`);
}
return await response.json() as {
data: Array<{ embedding: number[] }>;
usage?: { prompt_tokens: number; total_tokens: number };
};
} catch (error) {
if (attempt === this.maxRetries - 1) {
throw error;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 100));
}
}
throw new Error('Max retries exceeded');
}
}View on GitHub (pinned to fa13ee4ad6)
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)
Example fix
// before
const svc = new OpenAIEmbeddingService({ apiKey, model: 'text-embedding-ada-003' });
await svc.embedBatch(texts); // OpenAI API error: 404 - model not found
// after
const svc = new OpenAIEmbeddingService({ apiKey, model: 'text-embedding-3-small' });
await svc.embedBatch(texts); Defensive patterns
Strategy: retry
Validate before calling
function classifyOpenAiStatus(message: string): 'auth' | 'rate' | 'request' | 'notfound' | 'unknown' {
const m = message.match(/OpenAI API error: (\d{3})/);
if (!m) return 'unknown';
return { 401: 'auth', 403: 'auth', 429: 'rate', 400: 'request', 404: 'notfound' }[m[1]] ?? 'unknown';
} Try / catch
for (let attempt = 0; attempt < 5; attempt++) {
try {
return await svc.embedBatch(texts);
} catch (e) {
const kind = classifyOpenAiStatus(e instanceof Error ? e.message : '');
if (kind === 'auth' || kind === 'request') throw e; // do not retry client errors
if (attempt === 4) throw e; // retries already done in-library
await new Promise(r => setTimeout(r, 2 ** attempt * 500)); // extra backoff for 429/5xx
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- OpenAI embedding failed: ${message}
- Pinata upload failed: ${response.status} ${error}
- RATE_LIMIT
- Failed to import OpenAI
- Invalid completion type
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/56c63833599df643.
Report an issue: GitHub.