Recursive AI

Report errors with line and column

All oneyaml Go recipes

Validate a YAML file and say exactly what’s wrong and where. oneyaml’s errors are typed: match them with errors.As and read the position from their fields.

GO
package main
 
import (
	"errors"
	"fmt"
 
	oneyaml "oneyaml.dev/go"
)
 
type Config struct {
	Port    uint16 `yaml:"port"`
	Debug   bool   `yaml:"debug"`
	Workers int    `yaml:"workers"`
}
 
func main() {
	// Malformed YAML: a *SyntaxError with a line, a column and a category.
	err := oneyaml.Unmarshal([]byte("name: web\nports: [80, 443\n"), new(any))
	var se *oneyaml.SyntaxError
	if errors.As(err, &se) {
		fmt.Printf("%d:%d %s: %s\n", se.Line, se.Column, se.Category, se.Msg)
	}
 
	// A duplicate key says where the first one was.
	err = oneyaml.Unmarshal([]byte("name: web\nname: api\n"), new(any))
	if errors.As(err, &se) {
		fmt.Printf("%d:%d %q, first at %d:%d\n", se.Line, se.Column, se.Key, se.FirstLine, se.FirstColumn)
	}
 
	// Values that don't fit: every mismatch, each with its path and position.
	var cfg Config
	err = oneyaml.Unmarshal([]byte("port: 99999\ndebug: yes\nworkers: 4\n"), &cfg)
	var te *oneyaml.TypeError
	if errors.As(err, &te) {
		for _, e := range te.All() {
			fmt.Printf("%d:%d %s: %s\n", e.Line, e.Column, e.Path, e.Msg)
		}
	}
	fmt.Println("the rest is still read: workers =", cfg.Workers)
}
Text
3:1 syntax: expected ',' or ']' after an entry of the flow sequence opened at line 2, column 8 (is its closing ']' missing?)
2:1 "name", first at 1:1
1:7 /port: value `99999` overflows uint16
2:8 /debug: cannot unmarshal !!str `yes` into bool
the rest is still read: workers = 4

Good to know

  • *SyntaxError is YAML that isn’t well formed. Line and Column are 1-based, counted in characters; Offset is the byte offset. Switch on Category, not on Msg.
  • For a duplicate key, Key, FirstLine and FirstColumn say which key and where it first appeared.
  • A type mismatch doesn’t stop the read. The value that doesn’t fit is skipped and the rest is read. The *TypeError describes the first mismatch and All() lists every one, each with an RFC 6901 Path.
  • Limit errors are a *LimitError naming the Limit that was hit (read untrusted YAML safely). Every error matches its sentinel with errors.Is: ErrSyntax, ErrLimit, ErrNotFound and the others.
  • Error messages are written for people and may change in a minor release. Match errors by type, sentinel and constant.