A linguagem Go permite a criação de erros customizados para um melhor tratamento de erros. Para tal, precisamos importar o package errors:

Sintaxe
import "errors"

Assim, podemos declarar nossos novos tipos de erros como mostrado na sintaxe abaixo:

Ex 1:
func nome_erro() error {
  return errors.New("mensagem")
}

Quando utilizamos o método ou função New, criamos um erro em que único contexto do erro é uma mensagem de texto.

Sintaxe
import "errors"
func MeuErro() error{
     return errors.New("minha mensagem de erro")
}

Como declaramos nossos erros como funções, o que já foi estudado sobre o assunto é válido para o uso com erros customizados.

Como exemplo básico, um erro customizado pode ser exibido como mostrado no exemplo abaixo:

Ex 2:
package main
import "fmt"
import "errors"

func MeuErro() error{
     return errors.New("minha mensagem de erro")
}

func Funcao() (error) {
    return MeuErro()
}
func main() {
    erro := Funcao()
    fmt.Print("Erro:", erro)
}
Saída
Erro: minha mensagem de erro

Podemos utilizar o método Errorf do package fmt para imprimir em um string os erros ocorridos em cascata ou aninhados. Assim, podemos simular um stacktrace das mensagens de erro ocorridos.

Sintaxe
func Errorf(format string, a ...any) error
Ex 3:
package main
import "fmt"
import "errors"

func ArgumentoInvalido1() error{
     return errors.New("Argumento inválido1")
}

func ArgumentoInvalido2() error{
     return errors.New("Argumento inválido2")
}
//1o erro
func funcao1()(error) {
    return ArgumentoInvalido1() 
}
//2o erro
func funcao2()(error) {
    erro := funcao1();
    return fmt.Errorf("%w\n%w", erro,ArgumentoInvalido2())
}

func main() {
    erro :=funcao2();
    fmt.Print("erro:\n",erro);
}
Saída
erro:
Argumento inválido1
Argumento inválido2

Como nossas funções precisam retornar tanto o valor esperado quanto nosso erro customizado, internamente em nossas funções, devemos realizar as devidas validações para retorno de dados e retorno de erro de forma adequada.

Sintaxe
func nome_erro() error{
  return errors.New("mensagem erro")
}

func nome_funcao(parametros) (retorno,error){
   if(condicao){
     return resultado,nil  //OK
   }
  //outras validações
  return valor, nome_erro() //ERRO
}

No exemplo abaixo, criamos o erro ArgumentoInvalido para que possamos exibi-lo quando o argumento de nossa função não é o esperado retornando nosso erro customizado.

Ex 4:
package main
import "fmt"
import "errors"

//erro customizado
func ArgumentoInvalido() error{
     return errors.New("Argumento inválido")
}

//função que valida argumento retornando resultado e erro
func Dobro(p int) (int,error) {
    if(p != 0){ //se argumento válido
        return p*2, nil
    }
    return 0, ArgumentoInvalido()
}
func main() {
    _,erro := Dobro(0); //
    if(erro != nil){ //validação de erro
        fmt.Print("Erro:", erro)
    }
}
Saída
Erro:Argumento inválido

O package errors contém o método Is. Com esse método, podemos comparar um erro capturado ao nosso erro customizado como alternativa ao uso de validação com nil visto acima.

Sintaxe
func Is(err, target error) bool
onde:
err: erro capturado
target: erro cusmtomizado
Ex 5:
package main
import "fmt"
import "errors"

//erro customizado global
var ErroArgInvalido = errors.New("Argumento inválido")
//outros erros

func Dobro(p int) (int,error) {
    if(p != 0){
        return p*2, nil
    }
    return 0, ErroArgInvalido //retorno do erro 
}

func main() {
    _,erro := Dobro(0);
    if(errors.Is(erro,ErroArgInvalido)){ //validação com erros.Is
        fmt.Print("ArgumentoInvalido")
    }else{
        fmt.Print("OK")
    }
}
Saída
ArgumentoInvalido

Em alguns momentos, precisamos adicionar mais dados sobre os nossos erros customizados. Para isso, utilizamos struct em que adicionamos informações aos nossos erros:

Sintaxe
type nome_erro struct {
   //membros
}

Para a declaração de um novo tipo de erro, precisamos herdar da interface error mostrada abaixo:

Sintaxe
type error interface {
   Error() string
}

Definimos, por exemplo, as informações codigo e msg para armazenar o código do erro e uma mensagem de erro respectivamente.

Ex 6:
func (e MeuErro) Error() string {
   return fmt.Sprintf("codigo: %d msg %w", e.codigo, e.msg)
}

Assim, podemos declarar uma função que irá realizar as validações internas e retornar nosso erro customizado do tipo struct.

Abaixo, o código final contendo os trechos de código mostrados neste tópico:

Ex 7:
package main
import "fmt"
//import "errors"

type MeuErro struct{
codigo int
msg string
}

func (e MeuErro) Error() string {
   return fmt.Sprintf("codigo: %d\nmsg: %s", e.codigo, e.msg)
}

func funcao(p int) (int, error) {
   if p < 0 {
       return 0, MeuErro {codigo: 0, msg:"valor < 0 "}
   }
   return p*2, nil
}

func main() {
    _,erro :=funcao(-1);
    fmt.Print("erro:\n",erro.Error());
}
Saída
erro:
codigo: 0
msg: valor < 0

Utilizando ponteiro e referência, podemos retornar a struct contendo os dados do erro e acessar suas informações, como parte do tratamento, para exibição. O método errors.As do package errors é utilizado para validação do erro.

Modificamos o método Error do exemplo anterior para o mostrado abaixo:

Ex 8:
//metodo da interface error
func (e MeuErro) Error() string {
   return e.msg
}

Na função que retorna o erro adicionamos o retorno de uma referência ao nosso erro:

Ex 9:
//função p/ retorno de erro
func funcao(p int) (int, error) {
   if p < 0 {
       return 0, &MeuErro {codigo: 0, msg:"valor < 0 "} //referência p/ erro
   }
   return p*2, nil
}

Em nossa main, alteramos o tratamento adicionando o método errors.As para validação do nosso erro lançado e exibição de suas informações.

Sintaxe
func As(err error, target any) bool
Ex 10:
func main() {
    _,erro :=funcao(-1);
    var mErro *MeuErro; //ponteiro
    
    if errors.As(erro, &mErro) {
       fmt.Printf("msg:%s\ncodigo:%d",mErro.msg,mErro.codigo)
    }
}

Abaixo, o código final contendo os trechos de código mostrados neste tópico:

Ex 11:
package main
import "fmt"
import "errors"

//novo tipo
type MeuErro struct{
codigo int
msg string
}

//método da interface error
func (e MeuErro) Error() string {
   return e.msg
}

//função p/ retorno de erro
func funcao(p int) (int, error) {
   if p < 0 {
       return 0, &MeuErro {codigo: 0, msg:"valor < 0 "}
   }
   return p*2, nil
}

func main() {
    _,erro :=funcao(-1);
    var mErro *MeuErro; //ponteiro
    
    if errors.As(erro, &mErro) {
       fmt.Printf("msg:%s\ncodigo:%d",mErro.msg,mErro.codigo)
    }
}
Saída
erro:
codigo: 0
msg: valor < 0
  1. 07/04/2025 - versão inicial