abhigyanpatwari/GitNexus · error · HttpEmbeddingError
${err.terminalMessage}
Error message
${err.terminalMessage} What it means
An HttpEmbeddingError thrown from httpEmbedBatch's catch block when a RetryableEmbeddingBodyError (errors 123/124/125) has exhausted all retries inside resilientFetch. It surfaces the sentinel's terminalMessage verbatim (so the operator sees "unparseable response", "unexpected response shape", or the count-mismatch wording) and keeps the underlying parse error only in `cause`, ensuring the raw body text never reaches the sanitizeReason fallback or stderr. This is the terminal, user-visible form of a persistently bad 2xx body.
Source
Thrown at gitnexus/src/core/embeddings/http-client.ts:504
sleep: (ms) => abortableSleep(ms, requestOptions.signal),
},
},
);
} catch (err) {
if (
requestOptions.signal?.aborted ||
(err instanceof DOMException && err.name === 'AbortError')
) {
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 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 },
);View on GitHub (pinned to d540b00184)
Solutions
- Read the surfaced terminalMessage: if it says "unparseable", follow error 123's fixes; "unexpected response shape" → error 124; "N vectors for M texts" → error 125.
- Inspect err.cause for the raw parse error if you need the underlying detail (it is intentionally kept out of the user-facing message).
- Because retries are exhausted, the fault is deterministic — fix the endpoint configuration/schema rather than expecting another retry to help.
- Run the curl probe from error 123 against the configured URL+model to reproduce the bad body in isolation.
Example fix
// before: terminalMessage says "unparseable response" // (endpoint returns HTML) export GITNEXUS_EMBEDDING_URL=https://provider.example.com // after export GITNEXUS_EMBEDDING_URL=https://provider.example.com/v1
Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-call validation prevents a deterministic body fault; instead, probe once:
const r = await fetch(`${URL}/embeddings`, { /* ... */ });
try {
const j = await r.json();
if (!Array.isArray(j?.data)) throw new Error('bad shape');
} catch {
throw new Error('Endpoint body is not usable; see errors 123/124/125');
} Type guard
import { isHttpEmbeddingError } from 'gitnexus';
const isTerminalBodyError = (e: unknown): boolean =>
isHttpEmbeddingError(e) &&
e instanceof Error &&
(e.message.includes('unparseable response') ||
e.message.includes('unexpected response shape') ||
/vectors for \d+ texts/.test(e.message)); Try / catch
try {
await httpEmbed(texts);
} catch (e) {
if (isTerminalBodyError(e)) {
// retries exhausted on a bad body: this is deterministic, do not retry blindly;
// inspect e.cause for the raw parse error, then fix the endpoint config.
}
throw e;
} Prevention
- Probe the endpoint with curl at deploy time so deterministic body faults surface before a long run.
- Keep endpoint config (URL, model) in version-controlled env so a regression is visible in diff.
- Do not blindly retry a terminal body error — it indicates a config/schema problem, not a transient blip.
When it happens
Trigger: httpEmbedBatch after resilientFetch gives up: the fetchImpl callback threw RetryableEmbeddingBodyError on every attempt (maxAttempts, default HTTP_MAX_RETRIES+1 = 3). Whichever body fault (unparseable / wrong shape / count mismatch) recurred on the final attempt determines the message text carried in err.terminalMessage.
Common situations: Persistent wrong-endpoint configuration (always returns HTML). Provider schema mismatch that never resolves on retry. Provider input-count cap always truncating. Any of errors 123/124/125 that is deterministic rather than transient.
Related errors
- Embedding endpoint returned an unparseable response (${safeU
- Embedding endpoint returned an unexpected response shape (${
- Embedding endpoint returned ${received} vectors for ${expect
- Embedding endpoint returned ${err.response.status} (${safeUr
- Embedding endpoint circuit open (${safeUrl(url)}, batch ${ba
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/d5787f6bde955c21.
Report an issue: GitHub.