headroomlabs-ai/headroom · error · ConnectionError
OpenAI API failed after {self._max_retries} retries: {last_e
Error message
OpenAI API failed after {self._max_retries} retries: {last_error} What it means
Raised after OpenAIEmbedder's retry loop exhausts self._max_retries (default 3) attempts. Only transient errors — APIConnectionError, APITimeoutError, RateLimitError — are retried with exponential backoff (1s, 2s, 4s by default); this ConnectionError reports the last_error and means connectivity or rate limits persisted through every attempt.
Source
Thrown at headroom/memory/adapters/embedders.py:702
# Extract embeddings in order
embeddings = [np.array(item.embedding, dtype=np.float32) for item in response.data]
return embeddings
except (APIConnectionError, APITimeoutError, RateLimitError) as e:
last_error = e
delay = self.RETRY_DELAY_BASE * (2**attempt)
logger.warning(
f"OpenAI API error (attempt {attempt + 1}/{self._max_retries}): {e}. "
f"Retrying in {delay:.1f}s..."
)
await asyncio.sleep(delay)
except Exception as e:
# Non-retryable error
raise ConnectionError(f"OpenAI API error: {e}") from e
# All retries exhausted
raise ConnectionError(
f"OpenAI API failed after {self._max_retries} retries: {last_error}"
) from last_error
async def embed(self, text: str) -> np.ndarray:
"""Generate an embedding for a single text.
Args:
text: The text to embed.
Returns:
Normalized embedding vector as float32 numpy array.
Raises:
ConnectionError: If API call fails after retries.
"""
# Handle empty string
if not text or not text.strip():
return np.zeros(self.dimension, dtype=np.float32)View on GitHub (pinned to 322425c43b)
Solutions
- Wait for rate-limit windows to reset or upgrade the account's tier; the backoff (max ~3 tries) may be shorter than the 429 window.
- Increase the budget at construction: OpenAIEmbedder(max_retries=6) so backoff reaches ~32s.
- Batch less aggressively — add spacing between embed_batch() calls to stay under RPM limits.
- Check connectivity (curl https://api.openai.com/v1/models) and proxy/firewall rules if errors are connection-shaped.
- Fall back to LocalEmbedder/OllamaEmbedder for offline resilience.
Example fix
# before emb = OpenAIEmbedder() # default 3 retries, fails during rate limiting # after emb = OpenAIEmbedder(max_retries=6) # backoff reaches ~32s, survives longer 429 windows
Defensive patterns
Strategy: retry
Validate before calling
import os
# Right-size the retry budget before a batch job instead of the default 3.
batch_size = int(os.environ.get("EMBED_BATCH", "100"))
embedder = OpenAIEmbedder(max_retries=6 if batch_size > 50 else 3) Try / catch
async def embed_with_backoff(emb, text, outer_tries=3):
for i in range(outer_tries):
try:
return await emb.embed(text)
except ConnectionError as e:
if "failed after" in str(e) and i + 1 < outer_tries:
await asyncio.sleep(30 * (i + 1)) # outer backoff beyond inner retries
continue
raise Prevention
- Raise max_retries (construction arg) when running large batches against rate-limited tiers.
- Pace batch calls — sleep between embed_batch() calls to stay under RPM.
- Add an outer retry with minutes-scale backoff; the inner one only reaches ~7s total.
When it happens
Trigger: Calling embed()/embed_batch() during an outage, through a dead proxy, under sustained 429 rate limiting, or with DNS failure — each attempt fails with a retryable error until the budget is spent.
Common situations: Free-tier keys hitting embedding rate limits in batch jobs; regional API outage; corporate firewall blocking api.openai.com; flaky mobile tethers; retry budget too small for how long the incident lasts.
Related errors
- OpenAI API error: {e}
- Ollama API failed after {self._max_retries} retries: {last_e
- failed to download {final_url} after {attempts} attempts: {e
- Ollama API error: {e}
- openai_api_key is required when using OpenAI embedder backen
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/396e80563da99c15.
Report an issue: GitHub.