infiniflow/ragflow · error · PydanticCustomError

duplicate_uuids

duplicate_uuids

Error message

Duplicate ids: '{duplicate_ids}'

What it means

PydanticCustomError (code duplicate_uuids) from DeleteReq.validate_ids (api/utils/validation_utils.py:869-940), the base class for DeleteDatasetReq/DeleteDocumentReq batch-delete payloads. After each entry passes UUID validation it is normalized to a 32-char lowercase hex via validate_uuid1_hex; the validator then counts occurrences with Counter and rejects the request if any normalized id appears more than once. Duplicates are reported back verbatim in the message so the caller sees exactly which ids collide.

Source

Thrown at api/utils/validation_utils.py:938

        Security Notes:
            - Validates UUID format (any version)
            - Duplicate check prevents data injection
            - None handling maintains pipeline integrity
        """
        if v_list is None:
            return None

        ids_list = []
        for v in v_list:
            try:
                ids_list.append(validate_uuid1_hex(v))
            except PydanticCustomError as e:
                raise e

        duplicates = [item for item, count in Counter(ids_list).items() if count > 1]
        if duplicates:
            duplicates_str = ", ".join(duplicates)
            raise PydanticCustomError("duplicate_uuids", "Duplicate ids: '{duplicate_ids}'", {"duplicate_ids": duplicates_str})

        return ids_list


class DeleteDatasetReq(DeleteReq):
    """Request model for deleting datasets."""

    ...


class DeleteDocumentReq(DeleteReq):
    """Request model for deleting documents."""

    @field_validator("ids", mode="after")
    @classmethod
    def validate_ids(cls, v_list: list[str] | None) -> list[str] | None:
        """
        Validate document IDs without enforcing UUIDv1.

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Deduplicate the ids array client-side before sending (use set() or dict.fromkeys to preserve order).
  2. Normalize ids to lowercase 32-char hex before comparing so case/hyphen variants collapse.
  3. Inspect the '{duplicate_ids}' value in the error message to find which entry was repeated.

Example fix

# before
payload = {"ids": selected_ids}  # may contain repeats
# after
payload = {"ids": list(dict.fromkeys(selected_ids))}
Defensive patterns

Strategy: validation

Validate before calling

import uuid
normalized = [uuid.UUID(x).hex for x in ids]
if len(set(normalized)) != len(normalized):
    raise ValueError("duplicate dataset/document ids after normalization")

Type guard

def has_unique_uuid_ids(ids: list[str] | None) -> bool:
    if ids is None:
        return True
    seen = set()
    for x in ids:
        h = uuid.UUID(x).hex
        if h in seen:
            return False
        seen.add(h)
    return True

Try / catch

try:
    req = DeleteDatasetReq(ids=ids)
except ValidationError as e:
    if any(err["type"] == "duplicate_uuids" for err in e.errors()):
        ids = list(dict.fromkeys(ids))  # dedup and retry once
        req = DeleteDatasetReq(ids=ids)
    else:
        raise

Prevention

When it happens

Trigger: DELETE /api/v1/datasets or /api/v1/documents with an ids array containing the same UUID twice — including cases where the two strings differ only in case or hyphenation (e.g. 'ABC...' vs 'abc...', hyphenated vs bare hex) because normalization happens before the duplicate check.

Common situations: Front-end accumulating selection across pages and adding the same row twice; scripts concatenating id lists without dedup; mixed-format UUIDs (upper/lower case, with/without dashes) coming from different sources that normalize to the same id.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/a77e566d63a80d7f. Report an issue: GitHub.