abhigyanpatwari/GitNexus · error · HttpEmbeddingError

Embedding endpoint circuit open

Error message

Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s

What it means

httpEmbedBatch maps CircuitOpenError from resilientFetch to this message: the process-global 'embeddings-http' circuit breaker (shared across all batches) opened after repeated endpoint failures, and the breaker is refusing new attempts until its cooldown elapses (retry-after shown in seconds). Failing fast protects the endpoint and avoids multi-second hangs per batch.

Solutions

  1. Wait the stated retry window before issuing further embedding calls.
  2. Fix the endpoint (see accompanying failures in logs — status codes, timeouts) so the breaker closes on success.
  3. Scale or stabilize the endpoint if it trips the breaker under batch load; lower batch rate with GITNEXUS_EMBEDDING_MIN_INTERVAL_MS.
  4. Restart the process to reset the in-memory breaker after the endpoint is healthy (state is per-process).
Defensive patterns

Strategy: retry

Validate before calling

// breaker state is internal; pace requests to keep it closed
process.env.GITNEXUS_EMBEDDING_MIN_INTERVAL_MS = '250'; // pacing floor before big runs

Type guard

const isEndpointCircuitOpen = (e: unknown): boolean =>
  e instanceof Error && e.message.includes('Embedding endpoint circuit open');

Try / catch

try {
  vectors = await httpEmbed(texts);
} catch (e) {
  if (isEndpointCircuitOpen(e)) {
    const secs = Number(/retry in (\d+)s/.exec(e.message)?.[1] ?? 60);
    await new Promise((r) => setTimeout(r, (secs + 1) * 1000));
    return httpEmbed(texts); // one bounded retry after cooldown
  }
  throw e;
}

Prevention

When it happens

Trigger: Sustained endpoint failures (5xx storms, network errors, bad-body retries) earlier in the same process tripping the breaker; the next embed batch — or a new query in the same session — is then rejected immediately with the remaining cooldown.

Common situations: Endpoint outage or overload during a long analyze --embeddings run in HTTP mode; rerunning a query immediately after a cascade of failures.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/efba77786b865365. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/core/embeddings/http-client.ts:562

      throw new HttpEmbeddingError(
        `Embedding request cancelled (${safeUrl(url)}, batch ${batchIndex})`,
        { cause: err },
      );
    }
    // Retries are exhausted on a bad 2xx body. Surface the message the sentinel
    // carried, keeping the underlying parse error in `cause` only — the body
    // text must never reach the `sanitizeReason` fallback and leak to stderr.
    if (err instanceof RetryableEmbeddingBodyError) {
      throw new HttpEmbeddingError(err.terminalMessage, { cause: err.cause });
    }
    if (err instanceof RetryableEmbeddingTimeoutError) {
      throw new HttpEmbeddingError(
        `${err.message} after ${maxAttempts} attempt(s) (${safeUrl(url)}, batch ${batchIndex})`,
        { cause: err.cause },
      );
    }
    if (err instanceof CircuitOpenError) {
      throw new HttpEmbeddingError(
        `Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s`,
        { cause: err },
      );
    }
    if (err instanceof DOMException && err.name === 'TimeoutError') {
      throw new HttpEmbeddingError(
        `Embedding request timed out after ${timeoutMs}ms (${safeUrl(url)}, batch ${batchIndex})`,
        { cause: err },
      );
    }
    if (err instanceof ResilientFetchExhaustedError) {
      throw new HttpEmbeddingError(
        `Embedding endpoint returned ${err.response.status} (${safeUrl(url)}, batch ${batchIndex})`,
        { cause: err },
      );
    }
    const reason = sanitizeReason(err instanceof Error ? err.message : String(err), url, apiKey);
    const safeCause = new Error(reason);

View on GitHub (pinned to ac9a4e9abd)