python-attrs / python-attrs/cattrs

Validation errors from multiple fields are not in ClassValidationError

Open
#694 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

validation
Dominant language
Python
Stars
1.1k
Forks
159
Avg merge
12h 21m
Merged PRs (30d)
6

Description

python version: 3.13.7
attrs version: 25.4.0
cattrs version 25.3.0

When I try to structure a dict into a class, I only see a validation error from one field, when I would expect to see multiple.

Example:

import attrs
import cattrs

@attrs.define()
class Foo:
    a: str = attrs.field(validator=[attrs.validators.min_len(3)])
    b: str = attrs.field(validator=[attrs.validators.min_len(3)])

cattrs.structure(
    {"a": "a", "b": "b"},
    Foo,
)

I am expecting to see validation errors from fields a and b, but only see a:

Traceback (most recent call last):
  File "<cattrs generated structure __main__.Foo>", line 16, in structure_Foo
    return __cl(
      **res,
    )
  File "<attrs generated methods __main__.Foo>", line 28, in __init__
    __attr_validator_a(self, __attr_a, self.a)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.13/site-packages/attr/_make.py", line 3279, in __call__
    v(inst, attr, value)
    ~^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.13/site-packages/attr/validators.py", line 575, in __call__
    raise ValueError(msg)
ValueError: Length of 'a' must be >= 3: 1

During handling of the above exception, another exception occurred:

  + Exception Group Traceback (most recent call last):
  |   File "/home/lincoln/.config/JetBrains/PyCharm2025.2/scratches/scratch_2.py", line 12, in <module>
  |     cattrs.structure(
  |     ~~~~~~~~~~~~~~~~^
  |         {"a": "a", "b": "b"},
  |         ^^^^^^^^^^^^^^^^^^^^^
  |         Foo,
  |         ^^^^
  |     )
  |     ^
  |   File ".venv/lib/python3.13/site-packages/cattrs/converters.py", line 589, in structure
  |     return self._structure_func.dispatch(cl)(obj, cl)
  |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  |   File "<cattrs generated structure __main__.Foo>", line 19, in structure_Foo
  |     except Exception as exc: raise __c_cve('While structuring ' + 'Foo', [exc], __cl)
  |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | cattrs.errors.ClassValidationError: While structuring Foo (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "<cattrs generated structure __main__.Foo>", line 16, in structure_Foo
    |     return __cl(
    |       **res,
    |     )
    |   File "<attrs generated methods __main__.Foo>", line 28, in __init__
    |     __attr_validator_a(self, __attr_a, self.a)
    |     ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
    |   File ".venv/lib/python3.13/site-packages/attr/_make.py", line 3279, in __call__
    |     v(inst, attr, value)
    |     ~^^^^^^^^^^^^^^^^^^^
    |   File ".venv/lib/python3.13/site-packages/attr/validators.py", line 575, in __call__
    |     raise ValueError(msg)
    | ValueError: Length of 'a' must be >= 3: 1
    +------------------------------------

Plus when I pass that exception to cattrs.transform_exception the output list is

['invalid value @ $']

which seems lacking on detail of where the validation error happened.

I am expecting to see multiple because the docs say

When structuring a class, cattrs will gather any exceptions on a field-by-field basis and raise them as a cattrs.ClassValidationError, which is a subclass of BaseValidationError.

I am doing something wrong, or this not a feature of cattrs?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided Python reproducer against cattrs.structure and then inspect cattrs.transform_exception with the resulting ClassValidationError. Trace how validation failures from fields a and b are collected and transformed; done means both field errors appear in ClassValidationError and the transformed output identifies their locations.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.