diff --git a/src/attr/validators.py b/src/attr/validators.py index 0b1a29443..b1610738f 100644 --- a/src/attr/validators.py +++ b/src/attr/validators.py @@ -343,8 +343,13 @@ def __call__(self, inst, attr, value): if self.iterable_validator is not None: self.iterable_validator(inst, attr, value) - for member in value: - self.member_validator(inst, attr, member) + for idx, member in enumerate(value): + try: + self.member_validator(inst, attr, member) + except (ValueError, TypeError) as e: + # Re-raise with context about which member failed + msg = str(e).replace(f"'{attr.name}'", f"'{attr.name}[{idx}]'") + raise type(e)(msg) from None def __repr__(self): iterable_identifier = ( @@ -399,9 +404,21 @@ def __call__(self, inst, attr, value): for key in value: if self.key_validator is not None: - self.key_validator(inst, attr, key) + try: + self.key_validator(inst, attr, key) + except (ValueError, TypeError) as e: + msg = str(e).replace( + f"'{attr.name}'", f"'{attr.name}[key:{key!r}]'" + ) + raise type(e)(msg) from None if self.value_validator is not None: - self.value_validator(inst, attr, value[key]) + try: + self.value_validator(inst, attr, value[key]) + except (ValueError, TypeError) as e: + msg = str(e).replace( + f"'{attr.name}'", f"'{attr.name}[{key!r}]'" + ) + raise type(e)(msg) from None def __repr__(self): return f""