Skip to content

Custom Messages & Constraints

Custom Messages and Codes

Every constraint accepts message= and code=. Messages support format placeholders for any constructor argument plus {value}:

age: Annotated[int, Interval(ge=18, message="Must be at least {ge}, got {value}")]

The placeholder set is built from the constraint instance's fields, so {ge}, {lt}, {multiple_of}, {prefix}, etc. all resolve.


Custom Constraints

The MonkConstraint protocol is one method. Use @constraint to define your own:

from monk import constraint

@constraint
class StartsWithVowel:
    message: str | None = None

    def validate(self, value: str) -> None:
        if not value or value[0].lower() not in "aeiou":
            raise ValueError("Must start with a vowel.")

Full guide: Customization.


Next Steps