chroma-core/chroma · error · ChromaClientError

Bad request to ${input} with status: ${resp.statusText}

Error message

Bad request to ${input} with status: ${resp.statusText}

What it means

Select.from_dict() (operator.py:1315-1319) allows exactly one top-level key, 'keys', in a Select dict; any extra field raises ValueError with the offending key names. The dict form is intentionally minimal ({"keys": [...]}) because it mirrors what Select.to_dict() emits (operator.py:1275), so extra entries indicate the caller mixed fields from another part of the Search payload (filter, rank, limit, group_by) into the select object. This strictness catches payload-shape confusion early rather than silently ignoring typos.

Source

Thrown at clients/js/packages/chromadb-core/src/ChromaFetch.ts:63

 *  It is intended to be passed to the ApiApi constructor.
 */
export const chromaFetch: FetchAPI = async (
  input: RequestInfo | URL,
  init?: RequestInit,
): Promise<Response> => {
  try {
    const resp = await fetch(input, init);

    const clonedResp = resp.clone();
    const respBody = await clonedResp.json();
    if (!clonedResp.ok) {
      const error = createErrorByType(respBody?.error, respBody?.message);
      if (error) {
        throw error;
      }
      switch (resp.status) {
        case 400:
          throw new ChromaClientError(
            `Bad request to ${input} with status: ${resp.statusText}`,
          );
        case 401:
          throw new ChromaUnauthorizedError(`Unauthorized`);
        case 403:
          throw new ChromaForbiddenError(
            `You do not have permission to access the requested resource.`,
          );
        case 404:
          throw new ChromaNotFoundError(
            `The requested resource could not be found: ${input}`,
          );
        case 409:
          throw new ChromaUniqueError("The resource already exists");
        case 422:
          if (
            respBody?.message &&
            (respBody?.message.startsWith("Quota exceeded") ||

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Strip the select object down to {"keys": [...]} — that is the only accepted shape.
  2. Move misplaced fields to the right level: limit belongs on Search(limit=10) or the limit dict, aggregate belongs inside group_by, not select.
  3. If a wrapper decorates payloads with bookkeeping keys, strip them before decode: payload = {k: v for k, v in payload.items() if k == 'keys'} — but prefer fixing the wrapper so only schema fields travel.
  4. Validate payloads against Select.to_dict()'s output shape before sending: the key set must equal {'keys'}.

Example fix

// before
select = {"keys": ["#document"], "limit": 10, "offset": 0}
# ValueError: Unexpected keys in Select dict: {'limit', 'offset'}

# after
search = Search(select={"keys": ["#document"]}, limit=10)
Defensive patterns

Strategy: validation

Validate before calling

def select_payload_shape_ok(payload: dict) -> bool:
    return set(payload.keys()) <= {"keys"}

Type guard

def is_select_shaped(v: Any) -> TypeGuard[Dict[str, Any]]:
    return isinstance(v, dict) and set(v.keys()) <= {"keys"}

Try / catch

try:
    Select.from_dict(payload)
except ValueError as e:
    if "Unexpected keys" in str(e):
        payload = {k: v for k, v in payload.items() if k == "keys"}
        Select.from_dict(payload)
    else:
        raise

Prevention

When it happens

Trigger: Search(select={"keys": ["#document"], "limit": 10}) — a Search-level field nested inside select; {"keys": [...], "k": 3} — an Aggregate-style field leaking into Select; {"keys": [...], "document": True} from attempting per-key flags; copy-pasting a GroupBy dict ({"keys": ..., "aggregate": ...}) as the select argument, which also trips this after the keys pass.

Common situations: Flattening a Search.to_dict() blob and feeding a sub-object that still carries sibling fields; hand-authoring request payloads where nested-field boundaries are guessed; merging config snippets that each add a field to the same dict; wrappers that inject metadata (e.g. a version or comment key) into every payload sub-object.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/bc5416a2b15d2a08. Report an issue: GitHub.