# Yamale validates YAML against schemas that are themselves YAML

> Yamale is a 23andMe Python library that checks YAML documents against a schema written in a small YAML dialect, with includes, recursion, strict mode and structured validation results, under an MIT licence.

**23andMe/Yamale** — A schema and validator for YAML.

- Repository: https://github.com/23andMe/Yamale
- Stars: 776 · Forks: 102
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/23andme-yamale

## Schemas written in a small YAML dialect

Yamale is a schema and validator for YAML, and the unusual part is the schema language. A schema is itself a valid YAML file where every node ends in a string of Yamale syntax, so str() is a string validator and int(max=200) is an integer validator with a bound. A basic schema looks like this:

```yaml
name: str()
age: int(max=200)
height: num()
awesome: bool()
```

The point of writing schemas in YAML is that the people who own a configuration file can also own the contract for it, without learning a separate configuration format or a programming language for validation. The library needs Python 3.8 or later, PyYAML, and optionally ruamel.yaml if you want YAML 1.2 support. The package is published on PyPI as yamale, and the repository carries a SECURITY.md, a pre-commit configuration, tox for the test matrix and ruff for linting, which tells you the project treats this as a maintained library rather than a script. The version history shows 5.3.0 at the end of 2024, 6.0.0 in January 2025 and 6.1.0 in November 2025, with the last push on 2026-08-13.

## Includes and recursion cut out repeated structures

The feature that makes schemas maintainable is includes. A schema file may hold more than one YAML document, separated by ---, and the first document is the base schema while every additional document is treated as an include. That lets you define a structure once and reference it from several places, and it also allows recursion, so a structure can contain itself. The reference syntax is include with a name:

```yaml
person1: include('person')
person2: include('person')
---
person:
    name: str()
    age: int()
```

There is a second convention that catches people out. Every root node that is not in the first YAML document is treated as an include, so a document that looks like a mapping of new structures is interpreted as a set of reusable definitions rather than as data. That is a sensible rule once you know it, and a confusing one the first time you hit it, so it belongs in the onboarding notes for anyone writing schemas at your organisation. The upstream project points at its Examples section for more involved patterns than the basic ones shown here.

## Strict mode rejects unexpected keys by default

Yamale validates strictly unless you ask it not to, and the switch is a single flag. The command line exposes -x, --no-strict, whose help text reads Disable strict mode, unexpected elements in the data will be accepted. The practical consequence is that by default a document with an extra key is invalid, which is the behaviour you want for a configuration contract and the behaviour that will break the first time somebody adds a field to a real file without updating the schema. There is a second, related design choice in how the schema is located. When you point Yamale at a directory it searches for YAML files, and for each file it looks in the same directory for a schema, and if there is none it keeps walking up the directory tree until it finds one. If it cannot find a schema it says so rather than skipping the file. That upwards search is convenient for a repository with a root schema.yaml, and it is a trap in a tree where different subdirectories have different contracts, because a file can silently be validated against a schema from three levels up.

## The command line walks files and spawns child processes

The CLI covers a directory of files or individual paths, with the current directory as the default:

```bash
usage: yamale [-h] [-s SCHEMA] [-e PATTERN] [-p PARSER] [-n CPU_NUM] [-x] [-v] [-V] [PATH ...]
```

The options that matter for a CI job are the schema override, the exclusion pattern, the parser choice and the worker count:

```bash
  -x, --no-strict       Disable strict mode, unexpected elements in the data will be accepted.
  -p PARSER, --parser PARSER
                        YAML library to load files. Choices are "ruamel" or "pyyaml" (default).
  -n CPU_NUM, --cpu-num CPU_NUM
                        Number of child processes to spawn for validation. Default is 4. 'auto' to use CPU count.
```

The exclude option takes a Python regular expression and drops any file whose absolute path contains a match, which is how you keep templates or example directories out of the run. The default of four child processes means a directory of thousands of files is validated in parallel, and 'auto' switches to the machine's CPU count. The parser flag is the one that bites in practice: PyYAML is the default and follows YAML 1.1, while ruamel gives YAML 1.2 semantics, and a file that parses under one can behave differently under the other. Validation is exposed as a console script entry point, yamale=yamale.command_line:main, so the command is available wherever the package is installed.

## The API returns results you can iterate over

The Python API takes files or strings and separates the schema from the data:

```python
import yamale
schema = yamale.make_schema('./schema.yaml')

# Create a Data object
data = yamale.make_data('./data.yaml')

# Validate data against the schema. Throws a ValueError if data is invalid.
yamale.validate(schema, data)
```

You can also pass YAML text directly with the content= parameter instead of a path, which is what you want when the document arrives over the wire. The simple form raises a ValueError on failure, but the more useful form catches YamaleError and walks the results, each of which carries the offending data, the schema it was checked against and the list of errors:

```python
try:
    yamale.validate(schema, data)
except YamaleError as e:
    print('Validation failed!\n')
    for result in e.results:
        print("Error validating data '%s' with '%s'\n\t" % (result.data, result.schema))
        for error in result.errors:
            print('\t%s' % error)
    exit(1)
```

That distinction decides how useful the library is in a tool that reports problems back to a user, because a single ValueError message is for a human reading a terminal, while a list of structured results is for a program that wants to point at each offending key. The same parser choice applies here, with parser='ruamel' passed to make_schema and make_data when the file uses YAML 1.2 features.

## Schemas are code, so they carry a trust boundary

The most important line in the documentation is a warning that appears twice, once at the top of the README and again above the schema section. It says to ensure that your schema definitions come from internal or trusted sources, because Yamale does not protect against intentionally malicious schemas. That is not a casual caveat. A schema is a program in a small language, and a schema arriving from an untrusted source is a schema you are executing, so the safe pattern is the same one you would apply to any other executable input: schemas come from your own repository, they go through code review, and a schema pulled from a customer or a third party is not something you feed to the validator. The licence is MIT, with the licence text kept in the LICENSE file and the package metadata, which keeps adoption simple for a library you might vendor into an internal tool. On maintenance, the repository is not a one-off: the last push was on 2026-08-13, and 6.1.0 is the current published release, so a pinned dependency is not pinned to something abandoned.

## Conclusion

Adopt Yamale if your team already ships YAML configuration and wants declarative validation with readable errors instead of hand written checks in a script, and if you can commit to treating schema files as code that goes through review. Do not adopt it to validate untrusted schema input, because the documentation states plainly that Yamale does not protect against intentionally malicious schemas. Before you depend on it, confirm that your YAML dialect matches the parser you select, pyyaml by default or ruamel for YAML 1.2, and check that strict mode, which is on by default, does not reject keys your existing files carry.

## FAQ

### How do I install Yamale?

Run pip install yamale, or pip install yamale[ruamel] to pull in ruamel.yaml for YAML 1.2 support. A manual install is also documented, downloading the archive from GitHub, unzipping it and running python setup.py install. The library needs Python 3.8 or later and PyYAML.

### How do I write a Yamale schema with reusable structures?

Put extra YAML documents in the same schema file separated by ---, and reference them with include('name'), for example person1: include('person'). The first document is the base schema, and every root node outside it is treated as an include, which also allows recursion.

### Why does Yamale reject my file for having an extra key?

Yamale runs in strict mode by default, so unexpected elements in the data are not accepted. The command line switch -x, --no-strict disables strict mode, and the API can be called with the same behaviour changed if you need to accept unknown keys.

### How do I get detailed validation errors from the Yamale API?

Catch YamaleError instead of ValueError and iterate over e.results, where each result carries result.data, result.schema and a list of result.errors. The README also shows passing YAML text directly with the content= parameter of make_schema and make_data.

## Sources

- [23andMe/Yamale on GitHub](https://github.com/23andMe/Yamale)
- [Issues](https://github.com/23andMe/Yamale/issues)
- [License: MIT](https://github.com/23andMe/Yamale/blob/master/LICENSE)
- [README](https://github.com/23andMe/Yamale/blob/master/README.md)
- [Releases](https://github.com/23andMe/Yamale/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/23andme-yamale
