Validator Package Deep Dive
Try it Online
No installation required! Try the validator in your browser →
Table of Contents
- Overview
- Key Concepts
- API Styles
- Practical Examples
- Validation Coverage
- Validation Result Structure
- Configuration Reference
- Source Map Integration
- Best Practices
The validator package performs comprehensive validation of OpenAPI Specification documents against their declared version. It checks structural correctness, semantic constraints, format compliance, and best practices, producing detailed error reports with specification references for every issue found.
Overview
Validation ensures your OpenAPI documents are correct before using them for code generation, documentation, or API gateway configuration. The validator catches issues ranging from missing required fields to invalid reference targets, malformed URLs, and inconsistent parameter declarations.
The validator supports OAS 2.0 through OAS 3.2.0, automatically adapting its rules to match the declared specification version. Each validation error includes a reference to the relevant section of the OpenAPI Specification, making it easy to understand why something is flagged and how to fix it.
Key Concepts
Validation vs Parsing
Understanding the distinction between parsing and validation is important. The parser converts YAML or JSON into structured Go types, performing basic syntax checking but minimal semantic validation. The validator then examines the parsed structure for specification compliance.
This separation allows you to parse a document (which might have validation errors) to understand its structure, then validate it to identify issues. It also enables the 30x performance improvement when using ValidateParsed on pre-parsed documents.
Severity Levels
Validation issues are categorized by severity, helping you prioritize fixes and configure appropriate behavior for your workflow.
SeverityError indicates specification violations that make the document invalid according to the OpenAPI Specification. These must be fixed for the document to be compliant.
SeverityWarning indicates best practice violations or recommendations that don't prevent the document from being technically valid but may cause issues with tooling or API consumers. Examples include operations without descriptions, trailing slashes in paths, or deprecated patterns.
SeverityInfo indicates informational messages that may be useful for debugging or understanding validator behavior but don't require action.
SeverityCritical indicates severe issues that prevent further processing. These are rare and typically indicate fundamental document problems.
Strict Mode
By default, the validator reports errors separately from warnings, and a document is considered valid if it has no errors (warnings are allowed). Strict mode changes this behavior, treating warnings as errors and requiring the document to be warning-free to be considered valid.
Use strict mode in CI/CD pipelines where you want to enforce best practices, not just specification compliance.
API Styles
See also: Basic example, Strict mode example, Custom validation example on pkg.go.dev
Functional Options API
Best for one-off validations with inline configuration:
result, err := validator.ValidateWithOptions(
validator.WithFilePath("openapi.yaml"),
validator.WithIncludeWarnings(true),
validator.WithStrictMode(false),
)
Struct-Based API
Best for validating multiple documents with consistent configuration:
v := validator.New()
v.IncludeWarnings = true
v.StrictMode = true
result1, _ := v.Validate("api1.yaml")
result2, _ := v.Validate("api2.yaml")
Practical Examples
Basic Validation
The simplest use case validates a single specification file:
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/validator"
)
func main() {
result, err := validator.ValidateWithOptions(
validator.WithFilePath("openapi.yaml"),
validator.WithIncludeWarnings(true),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("OAS Version: %s\n", result.Version)
fmt.Printf("Valid: %v\n", result.Valid)
fmt.Printf("Errors: %d\n", result.ErrorCount)
fmt.Printf("Warnings: %d\n", result.WarningCount)
if !result.Valid {
fmt.Println("\nValidation Errors:")
for _, e := range result.Errors {
fmt.Printf(" [%s] %s: %s\n", e.Severity, e.Path, e.Message)
if e.SpecRef != "" {
fmt.Printf(" Spec: %s\n", e.SpecRef)
}
}
}
if result.WarningCount > 0 {
fmt.Println("\nWarnings:")
for _, w := range result.Warnings {
fmt.Printf(" [%s] %s: %s\n", w.Severity, w.Path, w.Message)
}
}
}
Example Input (with issues):
openapi: 3.0.3
info:
title: Test API
# Missing required 'version' field
paths:
/users/: # Trailing slash (warning)
get:
# Missing operationId (warning)
responses:
'200':
# Missing description (error)
/users/{userId}:
get:
operationId: getUser
# Missing declaration for 'userId' path parameter (error)
responses:
'200':
description: Success
Example Output:
OAS Version: 3.0.3
Valid: false
Errors: 3
Warnings: 2
Validation Errors:
[error] info.version: missing required field 'version'
Spec: https://spec.openapis.org/oas/v3.0.3.html#info-object
[error] paths./users/.get.responses.200: missing required field 'description'
Spec: https://spec.openapis.org/oas/v3.0.3.html#response-object
[error] paths./users/{userId}.get: Path template references parameter '{userId}' but it is not declared in parameters
Spec: https://spec.openapis.org/oas/v3.0.3.html#path-item-object
Warnings:
[warning] paths./users/: Path should not have a trailing slash
[warning] paths./users/.get: Operation should have an operationId
Strict Mode for CI/CD
Enforce both specification compliance and best practices:
package main
import (
"fmt"
"log"
"os"
"github.com/erraggy/oastools/validator"
)
func main() {
result, err := validator.ValidateWithOptions(
validator.WithFilePath("openapi.yaml"),
validator.WithIncludeWarnings(true),
validator.WithStrictMode(true), // Treat warnings as errors
)
if err != nil {
log.Fatalf("Validation failed: %v", err)
}
if !result.Valid {
fmt.Fprintf(os.Stderr, "❌ Validation failed\n")
fmt.Fprintf(os.Stderr, " Errors: %d\n", result.ErrorCount)
fmt.Fprintf(os.Stderr, " Warnings (treated as errors): %d\n", result.WarningCount)
// Print all issues
allIssues := append(result.Errors, result.Warnings...)
for _, issue := range allIssues {
fmt.Fprintf(os.Stderr, "\n[%s] %s\n", issue.Severity, issue.Path)
fmt.Fprintf(os.Stderr, " %s\n", issue.Message)
}
os.Exit(1)
}
fmt.Println("✓ Validation passed (including best practices)")
}
High-Performance Validation with Pre-Parsed Documents
When integrating validation into a pipeline that also uses other oastools packages, parse once and reuse the result for 30x faster validation:
package main
import (
"fmt"
"log"
"time"
"github.com/erraggy/oastools/parser"
"github.com/erraggy/oastools/validator"
)
func main() {
// Parse once
parseResult, err := parser.ParseWithOptions(
parser.WithFilePath("openapi.yaml"),
parser.WithValidateStructure(true),
)
if err != nil {
log.Fatalf("Parse failed: %v", err)
}
// Validate using pre-parsed document (30x faster)
start := time.Now()
valResult, err := validator.ValidateWithOptions(
validator.WithParsed(*parseResult),
validator.WithIncludeWarnings(true),
)
elapsed := time.Since(start)
if err != nil {
log.Fatalf("Validation failed: %v", err)
}
fmt.Printf("Validation completed in %v\n", elapsed)
fmt.Printf("Valid: %v\n", valResult.Valid)
// parseResult can now be used with other packages
// (fixer, converter, joiner, generator) without re-parsing
}
Validating Multiple Documents
For batch validation scenarios:
package main
import (
"fmt"
"log"
"path/filepath"
"github.com/erraggy/oastools/validator"
)
func main() {
// Create reusable validator
v := validator.New()
v.IncludeWarnings = true
v.StrictMode = false
// Find all OpenAPI files
files, err := filepath.Glob("specs/*.yaml")
if err != nil {
log.Fatal(err)
}
var failed []string
for _, file := range files {
result, err := v.Validate(file)
if err != nil {
log.Printf("Error validating %s: %v", file, err)
failed = append(failed, file)
continue
}
if result.Valid {
fmt.Printf("✓ %s (warnings: %d)\n", file, result.WarningCount)
} else {
fmt.Printf("✗ %s (errors: %d, warnings: %d)\n",
file, result.ErrorCount, result.WarningCount)
failed = append(failed, file)
}
}
if len(failed) > 0 {
fmt.Printf("\n%d/%d files failed validation\n", len(failed), len(files))
} else {
fmt.Printf("\nAll %d files passed validation\n", len(files))
}
}
Processing Validation Errors Programmatically
Extract and categorize validation issues for custom reporting:
package main
import (
"fmt"
"log"
"strings"
"github.com/erraggy/oastools/validator"
)
func main() {
result, err := validator.ValidateWithOptions(
validator.WithFilePath("openapi.yaml"),
validator.WithIncludeWarnings(true),
)
if err != nil {
log.Fatal(err)
}
// Categorize errors by location
bySection := make(map[string][]validator.ValidationError)
for _, e := range result.Errors {
// Extract top-level section from path
section := "document"
if parts := strings.SplitN(e.Path, ".", 2); len(parts) > 0 {
section = parts[0]
}
bySection[section] = append(bySection[section], e)
}
// Report by section
for section, errors := range bySection {
fmt.Printf("\n%s issues (%d):\n", strings.ToUpper(section), len(errors))
for _, e := range errors {
fmt.Printf(" • %s: %s\n", e.Path, e.Message)
}
}
// Identify patterns
missingFields := 0
invalidRefs := 0
formatErrors := 0
for _, e := range result.Errors {
switch {
case strings.Contains(e.Message, "missing required"):
missingFields++
case strings.Contains(e.Message, "does not resolve"):
invalidRefs++
case strings.Contains(e.Message, "Invalid"):
formatErrors++
}
}
fmt.Printf("\nError patterns:\n")
fmt.Printf(" Missing required fields: %d\n", missingFields)
fmt.Printf(" Invalid references: %d\n", invalidRefs)
fmt.Printf(" Format errors: %d\n", formatErrors)
}
Validation with Custom User Agent
When validating specifications from URLs, set a custom User-Agent:
package main
import (
"log"
"github.com/erraggy/oastools/validator"
)
func main() {
result, err := validator.ValidateWithOptions(
validator.WithFilePath("https://api.example.com/openapi.yaml"),
validator.WithUserAgent("MyValidationTool/1.0"),
validator.WithIncludeWarnings(true),
)
if err != nil {
log.Fatalf("Validation failed: %v", err)
}
log.Printf("Valid: %v, Load time: %v", result.Valid, result.LoadTime)
}
Integration with Fixer
A common pattern validates, identifies fixable issues, applies fixes, and re-validates:
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/fixer"
"github.com/erraggy/oastools/parser"
"github.com/erraggy/oastools/validator"
)
func main() {
// Parse once
parseResult, err := parser.ParseWithOptions(
parser.WithFilePath("openapi.yaml"),
)
if err != nil {
log.Fatal(err)
}
// Initial validation
valResult, _ := validator.ValidateWithOptions(
validator.WithParsed(*parseResult),
)
fmt.Printf("Initial validation: %d errors\n", valResult.ErrorCount)
if !valResult.Valid {
// Attempt automatic fixes
fixResult, err := fixer.FixWithOptions(
fixer.WithParsed(*parseResult),
fixer.WithInferTypes(true),
)
if err != nil {
log.Fatalf("Fixer failed: %v", err)
}
if fixResult.HasFixes() {
fmt.Printf("Applied %d fixes\n", fixResult.FixCount)
// Re-validate fixed document
// Create a new ParseResult from the fixed document
fixedParse := &parser.ParseResult{
Document: fixResult.Document,
Version: parseResult.Version,
OASVersion: parseResult.OASVersion,
}
valResult, _ = validator.ValidateWithOptions(
validator.WithParsed(*fixedParse),
)
fmt.Printf("After fixes: %d errors\n", valResult.ErrorCount)
}
}
if valResult.Valid {
fmt.Println("✓ Document is valid")
} else {
fmt.Println("✗ Manual fixes required:")
for _, e := range valResult.Errors {
fmt.Printf(" %s: %s\n", e.Path, e.Message)
}
}
}
Validation Coverage
The validator performs comprehensive checks across all document sections. Understanding what's validated helps you interpret results and know what to expect.
Info Object Validation
The validator checks that required fields are present and formats are correct.
Checked Fields:
- title (required)
- version (required)
- termsOfService (URL format when present)
- contact.url (URL format when present)
- contact.email (email format when present)
- license.url (URL format when present)
Path Validation
Paths receive thorough structural and semantic validation.
Path Template Checks:
- Properly formed parameter placeholders ({paramName})
- No unclosed braces
- No reserved characters in parameter names
- No consecutive slashes (//)
- Trailing slash warnings (best practice)
Path Parameter Consistency:
- Every {param} in the path template must have a corresponding parameter definition with in: path
- Path parameters must be marked as required: true
- Declared path parameters should be used in the template
Operation Validation
Each operation (GET, POST, PUT, etc.) is validated for completeness and correctness.
Required Checks:
- responses object must be present
- At least one response code or default response
- Each response must have a description
Best Practice Warnings:
- Missing operationId
- Missing summary or description
- Duplicate operationId values across operations
Parameter Validation
Parameters are checked for correct structure and usage.
Structural Checks:
- name is required
- in is required and must be one of: path, query, header, cookie
- Path parameters must have required: true
- Schema or content must be defined (OAS 3.x)
Format Checks: - Valid parameter types and formats - Consistent use of serialization styles
Schema/Definition Validation
Component schemas undergo structural validation.
Checked Items:
- Valid type values when present
- Correct format for the specified type
- $ref targets must resolve to existing schemas
- Nested schema structures (allOf, oneOf, anyOf, properties)
Reference Validation:
- $ref uses correct path format for the OAS version
- Referenced schemas exist in components/definitions
- No circular references that would cause infinite loops
Security Scheme Validation
Security definitions are validated for completeness.
Checked by Type:
- apiKey: name and in are required
- http: scheme is required
- oauth2: flows object with required flow properties
- openIdConnect: openIdConnectUrl is required and valid URL
Server Validation (OAS 3.x)
Server objects are checked for correct structure.
Checked Items:
- url is required and valid format
- Server variables have default values
- Variable placeholders in URL match defined variables
Validation Result Structure
type ValidationResult struct {
// Valid is true if no errors were found (warnings allowed)
Valid bool
// Version is the detected OAS version string (e.g., "3.0.3")
Version string
// OASVersion is the enumerated version
OASVersion parser.OASVersion
// Errors contains all validation errors
Errors []ValidationError
// Warnings contains all validation warnings (if IncludeWarnings is true)
Warnings []ValidationError
// Counts
ErrorCount int
WarningCount int
// Performance metrics
LoadTime time.Duration
SourceSize int64
// Document statistics
Stats parser.DocumentStats
}
type ValidationError struct {
// Path is the JSON path to the issue location
Path string
// Message describes the issue
Message string
// Severity indicates the issue level
Severity Severity
// SpecRef is a URL to the relevant specification section
SpecRef string
// Field is the specific field that has the issue (optional)
Field string
// Value is the problematic value (optional)
Value string
}
Configuration Reference
Validator Fields
type Validator struct {
// IncludeWarnings determines whether to include best practice warnings
IncludeWarnings bool // Default: true
// StrictMode treats warnings as errors
StrictMode bool // Default: false
// UserAgent for HTTP requests when fetching URLs
UserAgent string
}
Available Options
| Option | Description |
|---|---|
WithFilePath(string) |
Input file path or URL |
WithParsed(ParseResult) |
Pre-parsed document (30x faster) |
WithIncludeWarnings(bool) |
Include best practice warnings |
WithStrictMode(bool) |
Treat warnings as errors |
WithUserAgent(string) |
Custom User-Agent for HTTP requests |
Source Map Integration
When you need line numbers for IDE-friendly error reporting, enable source maps during parsing and pass them to the validator:
parseResult, _ := parser.ParseWithOptions(
parser.WithFilePath("openapi.yaml"),
parser.WithSourceMap(true),
)
result, _ := validator.ValidateWithOptions(
validator.WithParsed(*parseResult),
validator.WithSourceMap(parseResult.SourceMap),
)
for _, err := range result.Errors {
fmt.Printf("%s: %s\n", err.Location(), err.Message)
}
Best Practices
Always validate before using specifications for code generation, documentation, or gateway configuration. Invalid specifications can produce incorrect or broken outputs.
Use strict mode in CI/CD pipelines to enforce both specification compliance and best practices from the start. It's easier to maintain quality than to fix accumulated issues later.
Leverage the parse-once pattern when combining validation with other operations. Parse once, then pass the ParseResult to validator, fixer, converter, and other packages for significant performance improvements.
Include spec references in error reports when building developer-facing tooling. The SpecRef field provides direct links to documentation that helps developers understand and fix issues.
Categorize issues by severity in your reporting. Critical and Error issues must be fixed; Warnings should be addressed but don't block validity; Info messages are informational only.
Consider validation as part of your API design workflow. Validating specifications early catches issues before they propagate to generated code, documentation, or runtime systems.
Use warnings as guidance, not just rules. The best practice warnings reflect common patterns that improve API usability and tooling compatibility. Understanding why something is flagged helps you make informed decisions about whether to address it.
Learn More
For additional examples and complete API documentation:
- 📦 API Reference on pkg.go.dev - Complete API documentation with all examples
- ✅ Basic example - Validate an OpenAPI specification
- 🔒 Strict mode example - Enforce best practices
- ⚙️ Custom validation example - Process validation issues by severity