Help us shape the future of Go! Take the 2026 Go Developer Survey

uerr

package
v0.6.8 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package uerr is ursus's error type.

Error message quality is a feature, not a courtesy. A dataframe library is used interactively; the difference between

unknown column

and

ursus: select: unknown column "reveune"
  did you mean: "revenue"?
  available: id, ts, revenue, region, units

is the difference between a library people enjoy and one they tolerate. Every error that names a column MUST list the available columns and offer near matches; the helpers here make that the path of least resistance.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInternal    error = &kindSentinel{KindInternal}
	ErrSchema      error = &kindSentinel{KindSchema}
	ErrType        error = &kindSentinel{KindType}
	ErrValue       error = &kindSentinel{KindValue}
	ErrUnsupported error = &kindSentinel{KindUnsupported}
	ErrIO          error = &kindSentinel{KindIO}
	ErrResource    error = &kindSentinel{KindResource}
)

Sentinels for errors.Is. Use as: errors.Is(err, uerr.ErrSchema).

Functions

func Annotate

func Annotate(err error, op, node string) error

Annotate applies WithOp/WithNode to err if it is an *Error, and otherwise leaves it alone. Plan nodes call this as errors propagate outward.

func Catch added in v0.3.0

func Catch(errp *error, op string)

Catch recovers a panic into *errp, as FromPanic describes. It must be deferred DIRECTLY — `defer uerr.Catch(&err, op)` — because recover only stops a panic when called by the deferred function itself.

Without a panic it leaves *errp alone, so it costs nothing on the path that returns normally.

func Guard added in v0.3.0

func Guard[T any](op string, fn func() (T, error)) (v T, err error)

Guard calls fn, and returns a panic in it as an error.

func GuardErr added in v0.3.0

func GuardErr(op string, fn func() error) (err error)

GuardErr is Guard for a function that returns only an error.

func NearMatches

func NearMatches(want string, candidates []string, n int) []string

NearMatches returns up to n candidates that are plausible typos of want, best first.

Two signals, because they catch different mistakes:

  • Edit distance catches transpositions and single-character slips ("reveune" → "revenue"), which are what people actually type.
  • Case-insensitive equality and SEPARATOR SQUASHING catch the other common failure, which is not a typo at all but a naming-convention mismatch ("userid" → "user_id", "TS" → "ts"). This used to say "prefix containment", which is not implemented and never was; squash() strips _ - . and space.

The distance budget scales with length: one edit for short names, up to three for long ones. A fixed budget either floods short-name suggestions or misses long-name typos.

func Raise added in v0.3.0

func Raise(e *Error)

Raise panics with e on purpose, for a constructor whose signature has no error to return: a String column past what its 32-bit offsets hold, say. FromPanic and Attributed return e itself, whatever its kind, where an *Error panicked any other way is taken for a bug.

Every operator boundary recovers a panic (step 72), so a Raise surfaces as e from Collect and its siblings. One that reaches a caller's own goroutine — through a public constructor called directly — prints e's message.

Types

type Error

type Error struct {
	Kind  Kind
	Op    string   // "select", "filter", "with_columns"
	Msg   string   // the one-line statement of what is wrong
	Node  string   // plan node attribution, e.g. "Project#3"; set by WithNode
	Hints []string // one per line, indented under Msg
	Err   error    // wrapped cause, if any
}

Error is ursus's error type.

Op is the user-facing operation ("select", "filter", "cast"), not the Go function name. Node is set by the plan layer as the error propagates outward, so a user learns *where* in their query the failure was — something a bare error string cannot express.

func Attributed added in v0.3.0

func Attributed(v any, kind Kind, op, format string, args ...any) *Error

Attributed turns a value recovered from code ursus calls but did not write — a user's udf, or a decoder reading a file — into an error of that code's kind.

An internal *Error still passes through: raised by ursus, it is ursus's bug wherever it surfaced.

func DuplicateColumn

func DuplicateColumn(op, name string, firstAt, secondAt int) *Error

DuplicateColumn reports a name collision, naming both positions.

func FromPanic added in v0.3.0

func FromPanic(v any, op string) *Error

FromPanic turns a value recovered from ursus's own code into an internal error.

An internal *Error passes through unchanged: it is ursus's own assertion (Internalf, raised where a signature has no error to return), and it already says what it is. Anything else — a runtime error, a string, an *Error of another kind — is a bug in ursus too, and becomes KindInternal with the value as its cause.

func Internalf

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

Internalf reports a bug in ursus.

func New

func New(kind Kind, op, format string, args ...any) *Error

New builds an Error.

func UnknownColumn

func UnknownColumn(op, name string, available []string) *Error

UnknownColumn is the single most common error in a dataframe library, so it has a dedicated constructor that always produces the full diagnostic.

func Wrap

func Wrap(err error, kind Kind, op, format string, args ...any) *Error

Wrap attaches a cause.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Hint

func (e *Error) Hint(format string, args ...any) *Error

Hint appends a hint line. Returns e for chaining.

func (*Error) Is

func (e *Error) Is(target error) bool

Is supports errors.Is(err, uerr.Kind*) style matching via a sentinel per kind.

func (*Error) Unwrap

func (e *Error) Unwrap() error

func (*Error) WithNode

func (e *Error) WithNode(node string) *Error

WithNode sets plan-node attribution if not already set, same innermost-wins rule.

func (*Error) WithOp

func (e *Error) WithOp(op string) *Error

WithOp sets Op if it is not already set. The innermost operation wins, so an error raised inside expression typing keeps "cast" rather than being relabelled "select" by an outer frame.

type Kind

type Kind uint8

Kind classifies an error so callers can branch without string matching.

const (
	// KindInternal is a bug in ursus. Users should never legitimately see one.
	KindInternal Kind = iota
	// KindSchema is an unknown, duplicate, or ambiguous column.
	KindSchema
	// KindType is a dtype mismatch: an operation not defined for the given types.
	KindType
	// KindValue is a bad literal, an out-of-range cast, an unparseable pattern.
	KindValue
	// KindUnsupported is a well-formed request ursus cannot yet serve.
	KindUnsupported
	// KindIO is a failure reading or writing a data source.
	KindIO
	// KindResource is a limit ursus refused to exceed rather than a request it
	// could not understand: the query is well-formed and would have worked with
	// more memory or more spill space.
	//
	// It is its own kind because it is the one class of failure where the right
	// response is to change a knob and retry, and a caller should be able to
	// detect that without matching on a message.
	KindResource
)

func (Kind) String

func (k Kind) String() string

String names the kind, or reports the number for one that has no name.

The fallback used to be `default: return "internal"`, which is worse here than the identical hazard fixed for JoinKind in step 51 — because KindInternal is the ZERO VALUE, the default arm was legitimately reached for it and the omission read as deliberate. An eighth kind added without a table entry would have rendered as "internal", whose own doc says "a bug in ursus; users should never legitimately see one". A resource or IO failure mislabelled as an ursus bug is the wrong advice to give a user, and it is the failure mode ConcatMode.String's comment documents for eight enums; this is the ninth.

The `!= ""` half is the load-bearing one: the array is sized by kindCount, so a forgotten entry is the empty string rather than a missing index.

type PanicError added in v0.3.0

type PanicError struct {
	Value any    // what was passed to panic
	Site  string // the function and line that panicked, or "" if not found
	Stack []byte // the goroutine's stack at the recover

	// Callers are the functions from the panic site outward, the runtime's own left
	// out. It is how a recover decides whose code panicked: a Parquet read that
	// panicked inside arrow-go was reading a corrupt file, and one that panicked in
	// ursus is ursus's bug.
	Callers []string
}

PanicError is a recovered panic: the value, where it was raised, and the stack.

A panic is an error, never a dead process

ursus runs a query's expressions on worker goroutines, and a panic in a worker goroutine can be recovered by no one else — not the caller, not a test, not the program that asked the question. Until step 72 one panic in one row killed the host process. Every place ursus runs code now recovers around the CALL, and turns what it recovers into an ordinary error through FromPanic or Attributed.

func (*PanicError) Error added in v0.3.0

func (p *PanicError) Error() string

func (*PanicError) Unwrap added in v0.3.0

func (p *PanicError) Unwrap() error

Unwrap exposes a panic value that is itself an error, so errors.As finds a runtime.Error — a nil dereference, an index out of range — through the result.

Never an ursus *Error. Its Kind would then answer errors.Is for a kind that is not the result's: data.MustValues panics with a KindType error, and a caller asking errors.Is(err, ErrType) of the internal error it became would get true.

Jump to

Keyboard shortcuts

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