errs

package
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package errs defines the mio CLI's typed error model and the stable process exit codes that AI agents and CI branch on.

Every command path that can fail should return (or wrap) a *CLIError so that main.go can translate it into the correct exit code. The exit-code contract is part of the public CLI surface — see the design doc §4.5 — and must not drift.

Index

Constants

View Source
const (
	ExitOK          = 0 // success
	ExitGeneric     = 1 // generic / unexpected error
	ExitUsage       = 2 // bad flags / usage error
	ExitAuth        = 3 // missing or invalid credentials
	ExitNotFound    = 4 // resource not found (404)
	ExitNeedsConfir = 5 // destructive op needs --yes in a non-TTY
	ExitRateLimited = 6 // rate limited (429)
	ExitServer      = 7 // upstream server error (5xx)
)

Exit codes. These are a stable public contract: agents and CI scripts branch on them, so values must never be reused for a different meaning.

Variables

This section is empty.

Functions

func CodeOf

func CodeOf(err error) int

CodeOf extracts the exit code carried by err. A nil error is ExitOK; any error that is not (and does not wrap) a *CLIError is treated as ExitGeneric.

func ExitCodeForStatus

func ExitCodeForStatus(status int) int

ExitCodeForStatus maps an HTTP status code to a CLI exit code. Used by the client when an API call returns a non-2xx response so the exit code reflects the failure class even when the body has no structured error.

func HTTPStatusOf added in v0.13.0

func HTTPStatusOf(err error) int

HTTPStatusOf returns the HTTP status the API answered with for err, or 0 when err did not come from an HTTP response.

It walks the WHOLE error chain rather than reading only the outermost *CLIError, returning the first RECORDED status it finds from the outside in (wrappers that carry none are transparent). Commands routinely re-wrap a client error to add context — e.g. scaffold's errs.Wrap(errs.CodeOf(err), fmt.Errorf("page %q: %w", slug, err)) — and that outer wrapper has no status of its own. Stopping at the outermost error would silently drop the API's answer for exactly the paths that need it most, and adding context to an error does not make the server's verdict less true.

Note this deliberately differs from CodeOf, which reads only the outermost *CLIError: a command may intentionally RECLASSIFY the exit code (that is a local judgement about how the process should terminate) while the HTTP status remains a statement of fact about what the API returned.

Types

type CLIError

type CLIError struct {
	// Code is the process exit code (one of the Exit* constants).
	Code int
	// Err is the underlying cause; its Error() string is shown to the user.
	Err error
	// HTTPStatus is the status the API actually answered with, or 0 when this
	// error never came from an HTTP response (a genuinely local failure: bad
	// flags, unreadable file, no API key).
	//
	// Why this field exists (MIO-2656): the JSON:API error envelope main.go
	// writes to stderr used to reconstruct its `status` member from Code via a
	// reverse lookup. That reverse is lossy BY CONSTRUCTION, because
	// ExitCodeForStatus is many-to-one — 401 and 403 both collapse to ExitAuth,
	// and 400, 409 and 422 all collapse to ExitUsage. The envelope therefore
	// reported 403 as "401", and 409/422 as "400". Agents branch on
	// errors[].status: 403-vs-401 decides whether re-authenticating can help,
	// and 409-vs-422 decides whether retrying can help, so a rewritten status
	// is a broken machine contract. Carrying the real status alongside the
	// (deliberately coarse) exit code lets both be right at once.
	HTTPStatus int
}

CLIError carries both a human-facing error and the process exit code that should be returned when it propagates to main. It implements error and unwraps to the underlying cause so errors.Is/As keep working.

func New

func New(code int, format string, args ...any) *CLIError

New builds a *CLIError from a code and a formatted message.

func NewHTTP added in v0.13.0

func NewHTTP(status int, format string, args ...any) *CLIError

NewHTTP builds a *CLIError for a non-2xx HTTP response: the message is formatted like New, the exit code is derived from status via ExitCodeForStatus, and the status itself is retained on the error so the error envelope can report what the API really answered (MIO-2656).

Deriving the code here rather than taking it from the caller keeps Code and HTTPStatus in lockstep: they can never describe two different failures.

func Wrap

func Wrap(code int, err error) *CLIError

Wrap attaches an exit code to an existing error. If err is nil it returns nil so callers can wrap unconditionally.

func WrapHTTP added in v0.13.0

func WrapHTTP(status int, err error) *CLIError

WrapHTTP is NewHTTP for an existing cause: it attaches the status-derived exit code AND the status itself. If err is nil it returns nil so callers can wrap unconditionally.

func (*CLIError) Error

func (e *CLIError) Error() string

Error implements the error interface.

func (*CLIError) Unwrap

func (e *CLIError) Unwrap() error

Unwrap allows errors.Is / errors.As to reach the wrapped cause.

Jump to

Keyboard shortcuts

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