ErrLookupBackground articles › ValidationError explained: why open-source libraries reject your input — file uploads, YAML manifests, unique fields, and query permissions

ValidationError explained: why open-source libraries reject your input — file uploads, YAML manifests, unique fields, and query permissions

ValidationError is the structured rejection libraries throw when input, configuration, or content fails a validation gate before it reaches the database or business logic. Developers meet it as a failed file upload (disallowed MIME type, unsafe SVG, corrupt PDF, extension/content mismatch), an invalid extension manifest (wrong YAML types, unsafe paths, bad command names), a duplicate value on a unique-indexed field, a query referencing a field the user's role cannot access, or a value that breaks a required grammar. This article covers the family across 13 repositories — Payload, Strapi, Spec Kit, Playwright, Django, and others — how the message shapes differ, and the fixes that hold everywhere.

Distilled from 175 documented records across 13 repositories.

Background

ValidationError sits at the trust boundary between a caller and a library's internals. It is raised deliberately at a validation gate, before data reaches the database or deeper logic: Strapi validates content-manager queries (filters, sort, populate) against field-level RBAC permissions before they hit the DB; Payload inspects uploaded file contents — magic-byte MIME detection, SVG sanitization, PDF integrity — inside checkFileRestrictions; Django's validate_password runs every configured AUTH_PASSWORD_VALIDATOR and aggregates the failures. The class exists to fail fast with a structured, nameable error instead of a downstream crash: Payload converts raw MongoDB E11000 duplicate-key errors and SQL UNIQUE-constraint failures into per-field ValidationErrors so the API returns a clean validation failure rather than a 500, and Spec Kit guards manifest fields up front precisely because non-string values would later raise bare TypeErrors that bypass its malformed-manifest handlers.

From the caller's side the error almost always names the offender. Spec Kit's messages interpolate the field and the actual type received ('expected a string, got float'); Strapi reports the offending key and its path within the query tree; dianping/cat's bundled CSS validator literally enumerates the accepted grammar ("Expected (<length> | <percentage> | inherit) but found '...'"); Django's aggregator collects every validator's message and code into one error_list. Several libraries join multiple sub-errors into a single message — Payload's file-restriction error concatenates all failed content checks with ', ' — so reading the full message tells you exactly which sub-check fired.

The family splits into two broad groups. One validates user-supplied data and content: upload MIME allowlists and extension/content cross-checks (Payload, ECC), uniqueness constraints (Payload's duplicate-key and upsert handlers, slug collisions), password policy (Django), and CSS value grammar (cat). The other validates configuration, metadata, and authority: Spec Kit's extension-manifest checks cover path safety, naming patterns, section shapes, and hook structure; Strapi's api-token assertions enforce that tokens are created by an authenticated, existing admin and owned by that admin; Playwright's variants are internal protocol-integrity checks (channel types in SocksSupport messages, dispatcher types in serialized results) where the error usually signals a library bug or a build/version mismatch rather than anything the caller did wrong.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 155 more across the corpus — use search.

Honest provenance: generated on 2026-08-14 from AI-assisted analysis of the linked records. See how records are made.