chroma-core/chroma · error · ValueError

limit must be a non-negative integer

Error message

limit must be a non-negative integer

What it means

collection.delete(..., limit=n) caps how many matched records are deleted. SegmentAPI re-validates limit defensively ('validated upstream, but enforce defensively'): it must be a real int (bool is explicitly rejected, since isinstance(True, int) is True in Python) and non-negative. Violations raise ValueError('limit must be a non-negative integer').

Source

Thrown at chromadb/api/segment.py:807

            tenant=tenant,
            ids=ids,
            where=where,
            where_document=where_document,
        )

        self._manager.hint_use_collection(collection_id, t.Operation.DELETE)

        if (where or where_document) or not ids:
            ids_to_delete = self._executor.get(
                GetPlan(scan, Filter(ids, where, where_document))
            )["ids"]
        else:
            ids_to_delete = ids

        # Apply limit if specified (validated upstream, but enforce defensively)
        if limit is not None:
            if not isinstance(limit, int) or isinstance(limit, bool) or limit < 0:
                raise ValueError("limit must be a non-negative integer")
            if where is None and where_document is None:
                raise ValueError(
                    "limit can only be specified when a where or where_document clause is provided"
                )
            ids_to_delete = ids_to_delete[:limit]

        if len(ids_to_delete) == 0:
            return DeleteResult(deleted=0)

        records_to_submit = list(
            _records(operation=t.Operation.DELETE, ids=ids_to_delete)
        )
        self._validate_embedding_record_set(scan.collection, records_to_submit)
        self._producer.submit_embeddings(collection_id, records_to_submit)

        deleted_count = len(ids_to_delete)

        self._product_telemetry_client.capture(

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Normalize before the call: cast to int, reject bools, require >= 0 — or pass None to skip limiting
  2. Use None instead of -1/0-style sentinels when you mean 'no limit'
  3. Validate kwargs once in a wrapper so every delete path gets a clean limit

Example fix

// before
n = request.args.get('limit', -1)  # arrives as '-1' or -1
coll.delete(where=f, limit=n)

// after
raw = request.args.get('limit')
n = int(raw) if raw is not None else None
assert n is None or (isinstance(n, int) and not isinstance(n, bool) and n >= 0)
coll.delete(where=f, limit=n)
Defensive patterns

Strategy: validation

Validate before calling

def normalize_delete_limit(value):
    if value is None:
        return None
    if isinstance(value, bool) or not isinstance(value, int) or value < 0:
        raise ValueError(f'limit must be a non-negative int, got {value!r}')
    return value

coll.delete(where=f, limit=normalize_delete_limit(raw_limit))

Type guard

def is_valid_delete_limit(v) -> bool:
    return v is None or (isinstance(v, int) and not isinstance(v, bool) and v >= 0)

Prevention

When it happens

Trigger: coll.delete(where=..., limit=-1), limit=2.5, limit='10' (string from JSON/query params), or limit=True — any bool, non-int, or negative value passed as limit.

Common situations: Forwarding untyped user input (HTTP query params, config files) straight into limit; using -1 as an 'unlimited' sentinel carried over from SQL habits; JSON configs where numbers deserialize as strings.

Related errors


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