errs

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package errs is the single error vocabulary of an inceptools application.

A service layer never knows about HTTP. It returns an *errs.Error, and the transport decides what that becomes on the wire. There is no hierarchy of error types to learn — one concrete type with a machine-readable Code, a transport-neutral Kind, a caller-safe Message and an optional internal Cause that is never serialized.

The rule that makes this worth having: Message is the only field that reaches a client. Anything you must not leak — a driver string, an upstream reply, a query — goes in Cause, where it reaches the log and nothing else.

Example

A service decides what a failure means without importing net/http. The transport reads the Kind and picks a status.

package main

import (
	"fmt"

	"github.com/IncepTools/go/src/core/errs"
)

func main() {
	err := errs.Conflict("a product with the SKU %q already exists", "DESK-01").
		WithCode("SKU_TAKEN")

	fmt.Println(errs.KindOf(err))
	fmt.Println(errs.CodeOf(err))
	fmt.Println(err)
}
Output:
conflict
SKU_TAKEN
a product with the SKU "DESK-01" already exists

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CodeOf

func CodeOf(err error) string

CodeOf returns the machine code of err, or "" when it carries none.

func Is

func Is(err error, kind Kind) bool

Is reports whether err is (or wraps) an *Error of the given kind.

Example

The chain survives fmt.Errorf wrapping, so a caller several layers up can still branch on the kind.

package main

import (
	"fmt"

	"github.com/IncepTools/go/src/core/errs"
)

func main() {
	inner := errs.NotFound("product not found").WithCode("NOT_FOUND")
	outer := fmt.Errorf("loading the product page: %w", inner)

	fmt.Println(errs.Is(outer, errs.KindNotFound))
	fmt.Println(errs.CodeOf(outer))
}
Output:
true
NOT_FOUND

Types

type Detail

type Detail struct {
	Field   string `json:"field"`
	Message string `json:"message"`
	Rule    string `json:"rule,omitempty"`
}

Detail is one field-scoped explanation attached to an error. Validation failures are the common producer, but any error may carry them.

type Error

type Error struct {
	// Kind decides the transport status. Always set; zero value is treated as
	// KindInternal.
	Kind Kind
	// Code is a stable machine identifier a client may branch on, such as
	// "SKU_ALREADY_TAKEN". Optional, but strongly preferred over parsing Message.
	Code string
	// Message is caller-safe prose. It is the ONLY field that reaches a client.
	Message string
	// Details carry per-field explanations.
	Details []Detail
	// Cause is the internal error. Logged, never serialized.
	Cause error
	// Meta carries structured context for the log — ids, names, counts. Like
	// Cause, it does not reach the client.
	Meta map[string]any
}

Error is the one error type inceptools code returns across a layer boundary.

func Conflict

func Conflict(format string, args ...any) *Error

Conflict reports a state collision.

func Exhausted

func Exhausted(format string, args ...any) *Error

Exhausted reports a quota or rate limit.

func Forbidden

func Forbidden(format string, args ...any) *Error

Forbidden reports an authenticated caller without the permission.

func From

func From(err error) *Error

From normalises any error into an *Error. An error that is already one (or wraps one) is returned as-is; anything else becomes KindInternal with the original as Cause, so it is logged but not leaked.

Example

An error nobody classified is treated as internal, so its text cannot reach a client by accident.

package main

import (
	"errors"
	"fmt"

	"github.com/IncepTools/go/src/core/errs"
)

func main() {
	plain := errors.New("dial tcp 10.1.4.7:5432: connection refused")

	e := errs.From(plain)
	fmt.Println(errs.KindOf(e))
	fmt.Println(e.Message)
}
Output:
internal
internal error

func Internal

func Internal(format string, args ...any) *Error

Internal reports a defect. The message is NOT shown to clients by the web layer; it is replaced with a generic sentence and logged under the trace id.

func Invalid

func Invalid(format string, args ...any) *Error

Invalid reports a malformed request or a failed business rule.

func NotFound

func NotFound(format string, args ...any) *Error

NotFound reports a resource that does not exist OR is not visible to this tenant. Returning Forbidden for the second case confirms the resource exists to a caller who should not know; prefer NotFound for both.

func NotSupported

func NotSupported(format string, args ...any) *Error

NotSupported reports something this deployment will never do.

func PaymentRequired

func PaymentRequired(format string, args ...any) *Error

PaymentRequired reports insufficient credit or a declined charge.

func Timeout

func Timeout(format string, args ...any) *Error

Timeout reports a deadline exceeded.

func Unauthorized

func Unauthorized(format string, args ...any) *Error

Unauthorized reports a missing or unusable credential.

func Unavailable

func Unavailable(format string, args ...any) *Error

Unavailable reports a dependency that is down. Retrying may succeed.

func Wrap

func Wrap(kind Kind, message string, err error) *Error

Wrap turns any error into an *Error of the given kind, preserving the original as Cause. A nil error wraps to nil, so it is safe to write `return errs.Wrap(errs.KindInternal, "x", err)` on a possibly-nil err.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes Cause to errors.Is and errors.As.

func (*Error) WithCause

func (e *Error) WithCause(err error) *Error

WithCause returns a copy carrying an internal cause. The cause never reaches a client.

Example

The cause is for the log; the message is for the client. Everything in WithCause and WithMeta stays out of the response.

package main

import (
	"errors"
	"fmt"

	"github.com/IncepTools/go/src/core/errs"
)

func main() {
	driver := errors.New("pq: SSL is not enabled on the server (host=db-7.internal)")

	err := errs.Unavailable("the catalog is temporarily unavailable").
		WithCode("CATALOG_UNAVAILABLE").
		WithCause(driver).
		WithMeta("host", "db-7.internal")

	fmt.Println(err.Message)            // sent to the client
	fmt.Println(errors.Is(err, driver)) // still reachable for the log
}
Output:
the catalog is temporarily unavailable
true

func (*Error) WithCode

func (e *Error) WithCode(code string) *Error

WithCode returns a copy carrying a machine-readable code.

func (*Error) WithDetails

func (e *Error) WithDetails(d ...Detail) *Error

WithDetails returns a copy carrying field-scoped details.

Example

Field-scoped details are what a form attaches to an input.

package main

import (
	"fmt"

	"github.com/IncepTools/go/src/core/errs"
)

func main() {
	err := errs.Invalid("validation failed").
		WithCode("VALIDATION_FAILED").
		WithDetails(
			errs.Detail{Field: "name", Message: "name is required", Rule: "required"},
			errs.Detail{Field: "price", Message: "price must be 0 or more", Rule: "gte"},
		)

	for _, d := range err.Details {
		fmt.Printf("%s: %s\n", d.Field, d.Message)
	}
}
Output:
name: name is required
price: price must be 0 or more

func (*Error) WithMeta

func (e *Error) WithMeta(key string, value any) *Error

WithMeta returns a copy carrying one structured log field.

type Kind

type Kind string

Kind is the transport-neutral class of a failure. Adapters map it to their own status vocabulary (web/response maps it to HTTP).

const (
	KindInvalid      Kind = "invalid"       // malformed or failed validation
	KindUnauthorized Kind = "unauthorized"  // not authenticated
	KindForbidden    Kind = "forbidden"     // authenticated, not permitted
	KindNotFound     Kind = "not_found"     // no such resource, or not visible to this tenant
	KindConflict     Kind = "conflict"      // state collision, uniqueness, concurrent update
	KindExhausted    Kind = "exhausted"     // quota or rate limit
	KindPayment      Kind = "payment"       // payment or credit required
	KindUnavailable  Kind = "unavailable"   // dependency down, retry may succeed
	KindTimeout      Kind = "timeout"       // deadline exceeded
	KindNotSupported Kind = "not_supported" // never going to work here
	KindInternal     Kind = "internal"      // a defect; the default for an unclassified error
)

The kinds. Every *Error has one; the zero value is treated as KindInternal.

func KindOf

func KindOf(err error) Kind

KindOf reports the kind of any error, defaulting to KindInternal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL