ozzo-validation: Go Validation Without Struct Tags
An idiomatic Go (golang) validation package. Supports configurable and extensible validation rules (validators) using normal language constructs instead of error-prone struct tags.
At a glance
- What is it?
- ozzo-validation is an MIT-licensed Go package that expresses validation rules as ordinary Go values instead of struct tags. It suits API handlers and config loaders that need per-field error messages, and it is a poor fit for teams that want a schema language.
- Who is it for?
- Adopt ozzo-validation if your validation rules need to branch on runtime state, or if you want field-level error strings without a schema language. Skip it if you want validation driven entirely by struct tags, or if you need a schema artifact that other languages can consume.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 28 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Problem ozzo-validation Solves for Go Services
Go's struct tags are strings. A validator that reads tags has to parse them at runtime, and the compiler cannot check that a tag is spelled correctly or that its arguments make sense. ozzo-validation takes the other route: rules are Go values passed to functions, so the compiler sees them and an editor can complete them. The README states the package "use[s] normal programming constructs rather than error-prone struct tags to specify how data should be validated."
The audience is narrow but real. It is Go developers validating incoming request payloads, configuration maps, or domain structs who want an error that names the offending field. The README points to go-rest-api, a REST API starter kit, as an example of how the library is used in an application. If your validation lives in a handler that unmarshals JSON into a struct and then checks fields, that is the shape this package was built for. If your validation is a database constraint or a JSON Schema document, this is not the tool.
Two Entry Points: Validate and ValidateStruct
The package exposes two validation methods. validation.Validate() takes a single value and a list of rules. validation.ValidateStruct() takes a pointer to a struct and a list of field rules. The README is explicit about the pointer requirement: "when calling validation.ValidateStruct to validate a struct, you should pass to the method a pointer to the struct instead of the struct itself," and the same applies to validation.Field, which takes a pointer to the struct field.
Order is defined, not incidental. Validate() runs rules in the order listed and stops at the first failure, returning that error and skipping the rest. ValidateStruct() is different: fields are validated in the order specified, and when a field fails, an error is recorded for that field and validation continues with the next field. That is why the README's Address example returns two errors in one string: "Street: the length must be between 5 and 50; State: must be in a valid format." The first error is the length rule on Street; City passed; State failed its regexp.
Maps get their own constructor. validation.Map() with validation.Key() validates dynamic data, and keys nest: the README's example validates an Address sub-map inside a top-level map, producing "Address: (State: must be in a valid format; Street: the length must be between 5 and 50.); Email: must be a valid email address." Note that inside a map, the keys are validated in the order given, and a failing key does not stop the others.
Installing ozzo-validation v4 and Validating Your First Struct
The module path carries the major version. The README's installation command is:
go get github.com/go-ozzo/ozzo-validation/v4The go.mod in the repository declares module github.com/go-ozzo/ozzo-validation/v4 and requires Go 1.13 or above, which the README repeats under Requirements. The module depends on github.com/asaskevich/govalidator, which backs the is/ subpackage rules such as is.URL and is.Email.
A first real use is a struct with a Validate method. The README's Address type implements Validatable by defining Validate() error, and the rules are attached per field:
func (a Address) Validate() error {
return validation.ValidateStruct(&a,
validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
)
}With the README's sample values (Street "123", State "Virginia", Zip "12345"), calling a.Validate() prints "Street: the length must be between 5 and 50; State: must be in a valid format." The Zip value passes because "12345" matches five digits; the Street value fails the minimum length of 5; the State value fails the two-uppercase-letter regexp. If you want a scalar check instead, validation.Validate("example", validation.Required, validation.Length(5, 100), is.URL) returns "must be a valid URL" per the README, because the first two rules pass and the third fails.
Where ozzo-validation Gets in the Way
The library has no schema artifact. Rules live in Go source, so a rule set cannot be handed to a frontend, published as documentation, or validated by a non-Go service. Teams that treat the validation schema as a shared contract will find themselves maintaining a second copy elsewhere, and the two copies will drift.
The rule set is also bounded by what ships in the box. The repository contains rules such as required.go, length.go, match.go, minmax.go, multipleof.go, in.go, not_in.go, not_nil.go, absent.go, date.go, each.go, each_until_first_error.go, string.go, map.go and when.go, plus the is/ subpackage. If your format is not covered, you write a custom rule, which the README describes as "extremely easy" but which is still code you own and test.
Two smaller frictions are worth knowing before you start. The pointer requirement on ValidateStruct and Field is easy to get wrong and produces confusing results when you pass a value instead. And the module path changed at v4: UPGRADE.md exists in the repository for that reason, so an existing v3 codebase needs a migration rather than a version bump.
ozzo-validation Versus go-playground/validator
The obvious alternative is go-playground/validator, which the related searches surface alongside this package. The difference is where the rules live. go-playground/validator reads struct tags, so a field carries its constraints inline: a tag string on the field itself. ozzo-validation puts the rules in a Validate method or a call site, which means they can reference variables, call functions, and branch at runtime.
That trade-off cuts both ways. Tag-based validation is declarative and compact, and it travels with the struct definition, which some teams prefer for readability. Rule-based validation is more verbose but can express conditions a tag cannot, for example a rule that depends on another field's value or on configuration loaded at startup. The when.go file in this repository exists precisely for conditional rules, and that is the kind of logic a tag string cannot hold.
If your rules are static and uniform, tags are less code. If your rules depend on context, ozzo-validation is the better shape. Neither is a drop-in replacement for the other, so migrating means rewriting the rule definitions, not just the import path.
Maintenance, Licence and Upgrade Cost
The repository is not archived, and the last push was on 2026-09-03, so the project is being touched. The release history is uneven rather than steady: v4.3.0 landed on 2020-10-19, then nothing until v4.4.0, labelled "Revival Release", and v4.4.1, labelled "Performance: 36% fewer allocations", both on 2026-08-04. A five-year gap between v4.3.0 and v4.4.0 is the relevant fact for anyone planning around release cadence. The v4.4.1 note is the project's own characterisation of the change, not an independent measurement.
The licence is MIT, which permits commercial and closed-source use; the LICENSE file is at the repository root. This is not legal advice, and if your organisation has licence review requirements, the MIT text is short enough to read in full.
Upgrade cost depends on which major version you are on. The module path is github.com/go-ozzo/ozzo-validation/v4, and UPGRADE.md is the file the project provides for the transition. The README's requirement of Go 1.13 or above is the floor; the go.mod confirms it. If you depend on the is/ subpackage, remember it is backed by github.com/asaskevich/govalidator, so that dependency comes along.
Editorial conclusion
Adopt ozzo-validation if your validation rules need to branch on runtime state, or if you want field-level error strings without a schema language. Skip it if you want validation driven entirely by struct tags, or if you need a schema artifact that other languages can consume. Before committing, check that the rule set in the is/ package covers your formats, confirm your Go version is 1.13 or above, and read UPGRADE.md if you are moving from v3, since the module path changed to /v4.
Frequently asked questions
What is validation, and what does ozzo-validation do with it?
Validation is the process of checking that data satisfies a set of rules before it is used. ozzo-validation implements it for Go by letting you pass rules such as validation.Required and validation.Length to validation.Validate or validation.ValidateStruct, which return an error when a rule fails.
What is the role of a validator in ozzo-validation?
A validator is a rule that decides whether a value is acceptable. In this package the rules are Go values, for example validation.Required, validation.Length(5, 100) or is.URL, and Validate runs them in the order listed, stopping at the first failure.
What is the purpose of validation in programming, as ozzo-validation applies it?
The purpose is to reject invalid data and report why. ozzo-validation reports per-field errors, so a failed struct validation returns messages such as "Street: the length must be between 5 and 50; State: must be in a valid format."
ozzo validation is what kind of Go package?
It is a Go validation package that uses normal programming constructs rather than struct tags to specify rules, and it can validate structs, strings, byte slices, slices, maps and arrays, as well as custom types implementing the Validatable interface.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/go-ozzo-ozzo-validation)