abhigyanpatwari/GitNexus · error · HttpEmbeddingError

Embedding request failed

Error message

Embedding request failed (${safeUrl(url)}, batch ${batchIndex}): ${reason}

What it means

Thrown as HttpEmbeddingError when a batched POST to <GITNEXUS_EMBEDDING_URL>/embeddings fails at the transport layer (DNS failure, connection refused, TLS error, timeout, or aborted request). resilientFetch has already exhausted its retry budget (5xx/429 with capped backoff), so this error means the endpoint could not be reached successfully at all. The reason string is sanitized (sanitizeReason strips API keys) and the URL is redacted via safeUrl, so the message is safe to log.

Solutions

  1. Verify the endpoint is reachable from the same machine: curl -sS -o /dev/null -w '%{http_code}' "$GITNEXUS_EMBEDDING_URL/embeddings" (expect 4xx, not 000).
  2. Check for typos in GITNEXUS_EMBEDDING_URL (no scheme, wrong port, trailing garbage); the code appends /embeddings itself, so supply only the base URL such as http://localhost:11434/v1.
  3. If behind a proxy or VPN, make sure HTTPS_PROXY/HTTP_PROXY or DNS resolution works for the embedding host, or start the local embedding server (e.g. ollama serve).
  4. For slow endpoints, raise the timeout env var and retry budget before concluding the host is down.
  5. If embeddings are optional, unset GITNEXUS_EMBEDDING_URL and GITNEXUS_EMBEDDING_MODEL to fall back to the local ONNX embedder instead of the HTTP backend.

Example fix

# before
export GITNEXUS_EMBEDDING_URL=http://locahost:11434/v1   # typo: locahost
export GITNEXUS_EMBEDDING_MODEL=nomic-embed-text
gitnexus analyze  # -> Embedding request failed (http://locahost..., batch 0): ...

# after
export GITNEXUS_EMBEDDING_URL=http://localhost:11434/v1
gitnexus analyze
Defensive patterns

Strategy: try-catch

Validate before calling

// Fail fast before a long analyze run: probe the embedding endpoint.
import { isHttpMode } from './core/embeddings/http-client.js';

export async function assertEmbeddingEndpointReachable(): Promise<void> {
  if (!isHttpMode()) return;
  const base = process.env.GITNEXUS_EMBEDDING_URL!.replace(/\/+$/, '');
  const res = await fetch(`${base}/embeddings`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      model: process.env.GITNEXUS_EMBEDDING_MODEL,
      input: ['ping'],
    }),
    signal: AbortSignal.timeout(10_000),
  }).catch((err) => {
    throw new Error(`embedding endpoint unreachable: ${base} (${err.message})`);
  });
  if (res.status >= 500 || res.status === 429) {
    throw new Error(`embedding endpoint unhealthy: ${res.status}`);
  }
}

Type guard

import { HttpEmbeddingError } from './core/embeddings/http-client.js';

export const isHttpEmbeddingError = (e: unknown): e is HttpEmbeddingError =>
  e instanceof HttpEmbeddingError;

Try / catch

try {
  vectors = await httpEmbed(texts);
} catch (err) {
  if (isHttpEmbeddingError(err) && err.message.startsWith('Embedding request failed')) {
    // Transport-level failure; internal retries already exhausted by resilientFetch.
    // Log sanitized message (URL/api key already redacted) and surface a fix hint.
    throw new Error(
      `${err.message} — check GITNEXUS_EMBEDDING_URL reachability, proxy/DNS, ` +
      'and GITNEXUS_EMBEDDING_TIMEOUT_MS',
      { cause: err },
    );
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling httpEmbed() or httpEmbedBatch() while GITNEXUS_EMBEDDING_URL points to an unreachable host (typo in hostname, DNS not resolving), the embedding server is down, a proxy/firewall blocks the egress, the TLS certificate is invalid, or the per-request timeout (GITNEXUS_EMBEDDING_TIMEOUT_MS) is exceeded on every attempt. Also triggered when GITNEXUS_EMBEDDING_MAX_ATTEMPTS retries are all consumed by network resets.

Common situations: Running `gitnexus analyze` on a laptop with a corporate proxy that intercepts HTTPS; pointing GITNEXUS_EMBEDDING_URL at a localhost Ollama/LM Studio instance that is not started; using an internal DNS name that only resolves inside the VPN; a self-signed certificate on a private vLLM endpoint.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

        { 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);
    safeCause.name = err instanceof Error ? err.name : 'EmbeddingTransportError';
    throw new HttpEmbeddingError(
      `Embedding request failed (${safeUrl(url)}, batch ${batchIndex}): ${reason}`,
      { cause: safeCause },
    );
  }

  if (!resp.ok) {
    // resilientFetch already retried 5xx/429; any non-OK response here is
    // a terminal client error (4xx other than 429).
    throw new HttpEmbeddingError(
      `Embedding endpoint returned ${resp.status} (${safeUrl(url)}, batch ${batchIndex})`,
    );
  }

  if (parsed === undefined) {
    // Defensively unreachable: an OK response either sets `parsed` or throws
    // out of `fetchImpl`. Kept so the narrowing holds without a non-null
    // assertion, and so a future `resilientFetch` change can't return an
    // unvalidated body silently.

View on GitHub (pinned to ac9a4e9abd)