---
title: "Report errors with line and column"
url: https://recursiveai.org/oneyaml/go/cookbook/errors-with-line-and-column/
description: "Get the line, column and category of a YAML syntax error in Go, where a duplicate key was first defined, and every type mismatch in a document with its path."
version: "1.0.0"
---

# Report errors with line and column

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](https://recursiveai.org/oneyaml/go/cookbook/untrusted-input/)). 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.
