{"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":507,"sourceCode":"    );\n  } catch (err) {\n    if (\n      requestOptions.signal?.aborted ||\n      (err instanceof DOMException && err.name === 'AbortError')\n    ) {\n      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 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":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/embeddings/http-client.ts#L489-L525","documentation":"An HttpEmbeddingError thrown from httpEmbedBatch's catch block when resilientFetch raises a CircuitOpenError — the in-process circuit breaker for the shared key `embeddings-http` is open because the endpoint has been failing repeatedly. The breaker fails fast instead of issuing another request, and the message reports how long until the breaker half-opens (Math.ceil(retryAfterMs/1000) seconds). This protects the indexer from hammering a down endpoint.","triggerScenarios":"httpEmbedBatch called while the `embeddings-http` circuit breaker is open, which happens after a configured run of consecutive/recent failures (from any combination of retryable body errors, 5xx/429, timeouts, or network errors) within the breaker's window. Subsequent batches in the same process hit this fast-fail path until retryAfterMs elapses.","commonSituations":"Endpoint is down or returning 5xx; the first few batches exhausted retries and tripped the breaker; all remaining batches in the run then fail fast with this message. Restarting the process resets the in-process breaker, which can mask the issue temporarily.","solutions":["Wait for the reported retry window to elapse, then retry — the breaker will half-open and probe the endpoint.","Fix the underlying endpoint fault (the breaker opened for a reason): check connectivity, auth, model name, and provider status.","If you must reset immediately for testing, restart the indexer process (the breaker is in-process, keyed `embeddings-http`).","Reduce the failure rate by increasing GITNEXUS_EMBEDDING_RETRY_CAP_MS / GITNEXUS_EMBEDDING_MAX_ATTEMPTS so transient blips do not accumulate into a trip."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Cannot prevent a breaker trip from the caller, but you can avoid tripping it:\n// validate reachability and rate-limit before the run (see errors 130/131 probes).","typeGuard":"import { isHttpEmbeddingError } from 'gitnexus';\nconst isCircuitOpen = (e: unknown): boolean =>\n  isHttpEmbeddingError(e) && e instanceof Error && e.message.includes('circuit open');","tryCatchPattern":"// Wait out the reported window, then retry once. Do not busy-loop.\ntry {\n  await httpEmbed(texts);\n} catch (e) {\n  if (isCircuitOpen(e)) {\n    const secs = parseInt(/retry in (\\d+)s/.exec(e.message)?.[1] ?? '0', 10);\n    await new Promise(r => setTimeout(r, (secs + 1) * 1000));\n    // retry once; if it trips again, the endpoint is still down — back off further\n  } else throw e;\n}","preventionTips":["Fix the underlying endpoint fault — the breaker opened because failures accumulated.","Restarting the process resets the in-process breaker but does not fix the endpoint; avoid relying on that.","Tune GITNEXUS_EMBEDDING_MAX_ATTEMPTS / RETRY_CAP_MS so transient blips do not cascade into a trip."],"tags":["circuit-breaker","network","retry","endpoint","embeddings"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}