{"record":{"id":"efba77786b865365","repo":"abhigyanpatwari/GitNexus","slug":"embedding-endpoint-circuit-open-safeurl-url","errorCode":null,"errorMessage":"Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s","messagePattern":"Embedding endpoint circuit open \\((.+?), batch (.+?)\\): retry in (.+?)s","errorType":"exception","errorClass":"HttpEmbeddingError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/embeddings/http-client.ts","lineNumber":562,"sourceCode":"      throw new HttpEmbeddingError(\n        `Embedding request cancelled (${safeUrl(url)}, batch ${batchIndex})`,\n        { cause: err },\n      );\n    }\n    // Retries are exhausted on a bad 2xx body. Surface the message the sentinel\n    // carried, keeping the underlying parse error in `cause` only — the body\n    // text must never reach the `sanitizeReason` fallback and leak to stderr.\n    if (err instanceof RetryableEmbeddingBodyError) {\n      throw new HttpEmbeddingError(err.terminalMessage, { cause: err.cause });\n    }\n    if (err instanceof RetryableEmbeddingTimeoutError) {\n      throw new HttpEmbeddingError(\n        `${err.message} after ${maxAttempts} attempt(s) (${safeUrl(url)}, batch ${batchIndex})`,\n        { cause: err.cause },\n      );\n    }\n    if (err instanceof CircuitOpenError) {\n      throw new HttpEmbeddingError(\n        `Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s`,\n        { cause: err },\n      );\n    }\n    if (err instanceof DOMException && err.name === 'TimeoutError') {\n      throw new HttpEmbeddingError(\n        `Embedding request timed out after ${timeoutMs}ms (${safeUrl(url)}, batch ${batchIndex})`,\n        { cause: err },\n      );\n    }\n    if (err instanceof ResilientFetchExhaustedError) {\n      throw new HttpEmbeddingError(\n        `Embedding endpoint returned ${err.response.status} (${safeUrl(url)}, batch ${batchIndex})`,\n        { cause: err },\n      );\n    }\n    const reason = sanitizeReason(err instanceof Error ? err.message : String(err), url, apiKey);\n    const safeCause = new Error(reason);","sourceCodeStart":544,"sourceCodeEnd":580,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/embeddings/http-client.ts#L544-L580","documentation":"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.","triggerScenarios":"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.","commonSituations":"Endpoint outage or overload during a long analyze --embeddings run in HTTP mode; rerunning a query immediately after a cascade of failures.","solutions":["Wait the stated retry window before issuing further embedding calls.","Fix the endpoint (see accompanying failures in logs — status codes, timeouts) so the breaker closes on success.","Scale or stabilize the endpoint if it trips the breaker under batch load; lower batch rate with GITNEXUS_EMBEDDING_MIN_INTERVAL_MS.","Restart the process to reset the in-memory breaker after the endpoint is healthy (state is per-process)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// breaker state is internal; pace requests to keep it closed\nprocess.env.GITNEXUS_EMBEDDING_MIN_INTERVAL_MS = '250'; // pacing floor before big runs","typeGuard":"const isEndpointCircuitOpen = (e: unknown): boolean =>\n  e instanceof Error && e.message.includes('Embedding endpoint circuit open');","tryCatchPattern":"try {\n  vectors = await httpEmbed(texts);\n} catch (e) {\n  if (isEndpointCircuitOpen(e)) {\n    const secs = Number(/retry in (\\d+)s/.exec(e.message)?.[1] ?? 60);\n    await new Promise((r) => setTimeout(r, (secs + 1) * 1000));\n    return httpEmbed(texts); // one bounded retry after cooldown\n  }\n  throw e;\n}","preventionTips":["Stabilize/scale the endpoint before large embedding runs; the breaker is per-process and resets on restart.","Add pacing (GITNEXUS_EMBEDDING_MIN_INTERVAL_MS) to avoid tripping it under load.","Investigate the failures that opened it (5xx/network) rather than only honoring the cooldown."],"tags":["embeddings","http","circuit-breaker","rate-limiting","resilience"],"backgroundTag":"circuit-breaker-open","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}