Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Custom Validator

ASecurity

Build custom field validators for Protean domain elements — callable classes that validate individual field values. Covers format validation (email, phone, URL, SKU, credit card, etc.), using Protean's built-in RegexValidator, creating parameterized/configurable validators, composing multiple validators on a single field, and customizing error messages. Use when the user asks to "create a validator", "add custom validation", "validate email format", "validate phone number", "build a format ch...

45 stars
0 votes
0 copies
0 views
Added 9/20/2026
developmentpythongogit

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add proteanhq/protean --skill custom-validator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Custom Validator?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Custom Validator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/proteanhq-custom-validator/badge)](https://www.skillsdirectory.com/skills/proteanhq-custom-validator)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: custom-validator
description: Build custom field validators for Protean domain elements — callable classes that validate individual field values. Covers format validation (email, phone, URL, SKU, credit card, etc.), using Protean's built-in RegexValidator, creating parameterized/configurable validators, composing multiple validators on a single field, and customizing error messages. Use when the user asks to "create a validator", "add custom validation", "validate email format", "validate phone number", "build a format checker", "add a regex validator", or needs single-field validation beyond built-in constraints (required, max_length, min_value, choices). Custom validators are callable classes attached to fields via the validators=[] parameter and raise ValidationError on invalid input.
license: Apache-2.0
compatibility: Requires Python 3.11+, protean framework
metadata:
  author: proteanhq
  version: "0.1"
  category: element
---

# Custom Validator

Custom validators are callable classes that validate individual field values. They are the mechanism for enforcing format rules, patterns, and domain-specific constraints on a single field — beyond what built-in field parameters (`required`, `max_length`, `min_value`, `choices`) provide.

## Basic structure

A custom validator is a callable class with `__call__` that raises `ValidationError` on invalid input:

```python
from protean.exceptions import ValidationError

class PhoneValidator:
    """Validates phone number format."""

    def __init__(self):
        self.error = "Invalid phone number format"

    def __call__(self, value):
        cleaned = value.replace("-", "").replace(" ", "").replace("(", "").replace(")", "")
        if not cleaned.startswith("+"):
            raise ValidationError(self.error)
        digits = cleaned[1:]
        if not digits.isdigit() or not (10 <= len(digits) <= 15):
            raise ValidationError(self.error)
```

Attach to a field with the `validators` parameter:

```python
@domain.value_object
class Phone:
    number: String(required=True, max_length=20, validators=[PhoneValidator()])
```

## Key rules

1. **Validators are callable classes** — Implement `__init__` and `__call__`. The `__call__` method receives the field value as its single argument.
2. **Raise `ValidationError` on failure** — Import from `protean.exceptions`. The error message string gets wrapped into `{field_name: [message]}` automatically.
3. **Return nothing on success** — If the value is valid, simply return (no return value needed).
4. **Validators run AFTER type casting** — The value passed to `__call__` has already been cast to the field's native type (e.g., `str` for `String`, `int` for `Integer`).
5. **Multiple validators are chained** — Use `validators=[V1(), V2()]`; all validators run, errors are collected.
6. **Validators are for single-field rules** — For cross-field validation, use `@invariant.post` instead (see [add-validation](../add-validation/SKILL.md)).
7. **Keep validators reusable** — Make them configurable via `__init__` parameters so they work across multiple fields and domain elements.

## Validation execution order

When a field value is set, Protean validates in this order:

1. **Empty check** — `required` field without value raises error
2. **Choices check** — Value must be in `choices` enum/list
3. **Type casting** — `_cast_to_type()` converts to native type
4. **Validators** — All validators in `validators=[]` list run sequentially

Custom validators execute at step 4, after the value is already type-cast and choice-validated.

## Built-in validators

Protean provides these validators out of the box (used internally by fields):

| Validator | Used by | Purpose |
|-----------|---------|---------|
| `MinLengthValidator(n)` | `String(min_length=n)` | Minimum character count |
| `MaxLengthValidator(n)` | `String(max_length=n)` | Maximum character count |
| `MinValueValidator(n)` | `Integer(min_value=n)`, `Float(min_value=n)` | Minimum numeric value |
| `MaxValueValidator(n)` | `Integer(max_value=n)`, `Float(max_value=n)` | Maximum numeric value |

These are applied automatically — you don't need to add them to `validators=[]`.

## Using RegexValidator

For pattern-based validation, use Protean's built-in `RegexValidator`:

```python
from protean.fields.validators import RegexValidator

@domain.value_object
class ProductCode:
    code: String(
        required=True,
        max_length=10,
        validators=[
            RegexValidator(
                regex=r"^[A-Z]{3}-\d{4}$",
                message="Product code must be in format XXX-9999"
            )
        ]
    )
```

`RegexValidator` supports:
- `regex` — Pattern string or compiled regex
- `message` — Custom error message (default: "invalid value")
- `inverse_match` — If `True`, fails when pattern DOES match
- `flags` — Regex flags (only when `regex` is a string)

## Parameterized validators

Make validators configurable via constructor parameters:

```python
class AllowedDomainValidator:
    """Validates email belongs to allowed domains."""

    def __init__(self, allowed_domains):
        self.allowed_domains = allowed_domains
        self.error = f"Email must belong to one of: {', '.join(allowed_domains)}"

    def __call__(self, value):
        domain_part = value.split("@")[-1].lower()
        if domain_part not in self.allowed_domains:
            raise ValidationError(self.error)

# Reuse with different configurations
corporate_email: String(validators=[AllowedDomainValidator(["company.com", "corp.com"])])
partner_email: String(validators=[AllowedDomainValidator(["partner.org", "vendor.net"])])
```

## Composing multiple validators

Chain validators for layered validation — all validators run and errors are collected:

```python
@domain.value_object
class Username:
    value: String(
        required=True,
        min_length=3,
        max_length=30,
        validators=[
            RegexValidator(
                regex=r"^[a-zA-Z][a-zA-Z0-9_]*$",
                message="Username must start with a letter, only letters/digits/underscores"
            ),
            ReservedWordValidator(reserved=["admin", "root", "system"]),
        ]
    )
```

## Common mistakes

- **Raising `ValueError` instead of `ValidationError`** — Always use `from protean.exceptions import ValidationError`. `ValueError` will not be caught by Protean's validation pipeline correctly.
- **Using validators for cross-field rules** — Validators only see one field's value. For rules involving multiple fields (e.g., "end_date > start_date"), use `@invariant.post` instead.
- **Duplicating built-in constraints** — Don't write a validator for `min_length` or `max_value` — use the field's built-in parameters.
- **Not making validators reusable** — Pass configuration through `__init__`, not hardcoded values.

## Complete examples

- [Basic validators (phone, URL)](assets/custom_validator_basic.py)
- [RegexValidator usage](assets/custom_validator_regex.py)
- [Parameterized validators](assets/custom_validator_parameterized.py)
- [Composing multiple validators](assets/custom_validator_composition.py)

## Detailed references

- [Basic Validators](references/basic-validators.md) - Building callable validator classes from scratch
- [Regex Validators](references/regex-validators.md) - Using Protean's RegexValidator for pattern matching
- [Parameterized Validators](references/parameterized-validators.md) - Configurable validators with constructor params
- [Composing Validators](references/composing-validators.md) - Chaining validators and error message customization
- [Anti-patterns](references/anti-patterns.md) - Common mistakes and how to avoid them

## Related skills

- [value-object](../value-object/SKILL.md) - Value objects often use custom validators
- [add-field](../add-field/SKILL.md) - Adding fields with validation to domain elements
- [add-validation](../add-validation/SKILL.md) - Choosing the right validation layer (field vs invariant vs guard)
- [aggregate](../aggregate/SKILL.md) - Aggregates can use field validators too

Attribution

proteanhqproteanhq
View sourceMore from proteanhq →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →