abhigyanpatwari/GitNexus · error · RetryableEmbeddingBodyError

Embedding endpoint returned ${received} vectors for ${expect

Error message

Embedding endpoint returned ${received} vectors for ${expected} texts (${safeEndpoint}, batch ${batchIndex})

What it means

A module-private RetryableEmbeddingBodyError thrown inside the retried fetch callback when the body is well-shaped (`data` is an array of embedding items) but its cardinality does not match the request — `payload.data.length !== batch.length`. This catches a 200 carrying `{"data": []}` (vacuously passing the every(isEmbeddingItem) check) or a half-truncated vector list. It is retried and counted against the circuit breaker (#2790), then surfaced terminally as HttpEmbeddingError (127). The message names both counts: e.g. "0 vectors for 64 texts".

Source

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

            // `retryable-network` (retried, then `breaker.recordFailure()`): the
            // same timeout would take 3 attempts instead of 1, count toward the
            // process-global `embeddings-http` breaker, and reach the operator as
            // "unparseable response" so they never reach for the timeout knob.
            if (isTerminalNetworkError(err)) throw err;
            throw new RetryableEmbeddingBodyError(unparseableMessage(), { cause: err });
          }
          if (!Array.isArray(payload?.data) || !payload.data.every(isEmbeddingItem)) {
            throw new RetryableEmbeddingBodyError(unexpectedShapeMessage());
          }
          // Cardinality belongs *inside* the retry loop. `every(isEmbeddingItem)`
          // is vacuously true for `[]` and true for any array shorter than the
          // request, so a 200 carrying `{"data": []}` — or half the vectors —
          // used to be classified `success`, call `breaker.recordSuccess()`
          // (erasing the outage signal), and only then fail terminally after a
          // single attempt. A short body is a truncated body: same backoff, same
          // breaker accounting as any other endpoint fault (#2790).
          if (payload.data.length !== batch.length) {
            throw new RetryableEmbeddingBodyError(
              countMismatchMessage(payload.data.length, batch.length, safeUrl(url), batchIndex),
            );
          }
          parsed = payload.data;
          return attemptResp;
        },
        breakerKey: HTTP_BREAKER_KEY,
        retry: {
          maxAttempts,
          baseDelayMs: HTTP_RETRY_BACKOFF_MS,
          capDelayMs: retryCapMs,
          retryAfterCapMs: retryCapMs,
          sleep: (ms) => abortableSleep(ms, requestOptions.signal),
        },
      },
    );
  } catch (err) {
    if (

View on GitHub (pinned to d540b00184)

Solutions

  1. Reduce the effective batch size by embedding smaller text lists in your own driver, or check the provider's per-request input limit and request a raise.
  2. Inspect a raw multi-input response with curl to see whether the provider is truncating or filtering inputs.
  3. Pre-truncate over-long input texts to the model's token limit so the provider does not drop them.
  4. If intermittent (truncation under load), the retry loop should recover it; persistent short bodies indicate a provider cap or filter to fix upstream.

Example fix

// before: provider caps inputs at 32, GitNexus sends 64
// (no direct env; the mismatch is reported per batch)

// after: there is no public batch-size env, so front the endpoint
// with a proxy that splits requests, or open a request for a
// configurable HTTP_BATCH_SIZE override upstream.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the provider returns one vector per input at your batch size:
const r = await fetch(`${URL}/embeddings`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${KEY}` },
  body: JSON.stringify({ model: MODEL, input: ['a', 'b', 'c', 'd'] }),
});
const body = await r.json();
if (!Array.isArray(body?.data) || body.data.length !== 4) {
  throw new Error(`Provider returned ${body?.data?.length ?? 0} vectors for 4 inputs`);
}

Type guard

import { isHttpEmbeddingError } from 'gitnexus';
const isCountMismatch = (e: unknown): boolean =>
  isHttpEmbeddingError(e) &&
  e instanceof Error &&
  /vectors for \d+ texts/.test(e.message);

Try / catch

try {
  await httpEmbed(texts);
} catch (e) {
  if (isHttpEmbeddingError(e) && /vectors for \d+ texts/.test(e.message)) {
    // provider is truncating/dropping inputs; reduce load or pre-filter inputs
  }
  throw e;
}

Prevention

When it happens

Trigger: httpEmbedBatch's fetchImpl after shape validation: payload.data.length differs from batch.length. Concrete cases: endpoint silently drops inputs it deems too long and returns fewer vectors; endpoint has a hard input-count cap lower than HTTP_BATCH_SIZE (64) and truncates; truncated stream yields a partial array; empty `data: []` response.

Common situations: Provider limits inputs per request to fewer than 64 (GitNexus's batch size) and returns only the accepted ones. Some inputs exceed the provider's max token length and are dropped without error. Network truncation cuts the JSON mid-array. Provider returns [] for a rate-limited or filtered batch.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/9699084ca49e0012. Report an issue: GitHub.