Standard library · System and tools

error

Structured errors with a kind, a message, a chain of causes, metadata and a retry policy.

import error

A function that may fail can return an error.Error: either "nil", meaning everything went well, or an error with a kind, such as "not found", and a message. The kind is for the code to decide on; the message is for a person to read.

When an error comes up from a lower layer, Wrap it so the current layer's explanation is added. The final message shows the whole chain, as in "could not save the report: the disk is full", and RootMessage gives the original cause.

For transient errors, such as a dropped network connection, RetryPolicy says when and how often to try again and how long to wait between attempts; the delay doubles each time.

Types

Error

struct Error
An error.
NameTypeDescription
kindstr = ""The kind of the error: one of the constants above, or one you define yourself.
messagesVectorThe message from each layer, from the innermost cause to the outermost.
kindsVectorThe kind of each layer.
traceVectorThe places the error has passed through.
meta_keysVectorThe metadata keys.
meta_valsVectorThe metadata values.
error-New.salam
import error

func find_user(id: int): error.Error:
    if id <= 0:
        ret error.New(error.KindInvalidArgument, "the id has to be positive")
    end
    if id > 100:
        ret error.NewAt(error.KindNotFound, "no such user", "find_user")
    end
    ret error.Nil()
end

func main:
    each id in [7, -1, 500]:
        mut e := find_user(id)
        if error.Ok(e):
            println id, "found", error.IsNil(e)
        else:
            println id, error.Kind(e), error.Message(e)
            println error.Is(e, error.KindNotFound), error.IsNotFound(e), error.IsInvalidArgument(e)
        end
        error.Free(e)
    end
    mut args := Vector {} as Vector<str>
    defer args.free()
    args.push("config.ini")
    e2 := error.Newf(error.KindIO, "cannot read {}", args)
    println error.Message(e2)
end
Output7 found true -1 invalid_argument the id has to be positive false false true 500 not_found no such user true true false cannot read config.ini

RetryPolicy

struct RetryPolicy
A retry policy.
NameTypeDescription
max_attemptsint = 3The greatest number of attempts; three by default.
base_delay_msint = 100The delay after the first failed attempt, in milliseconds; 100 by default.
max_delay_msint = 5000The ceiling on the delay, in milliseconds; 5000 by default.
error-RetryPolicy.salam
import error

func main:
    mut policy := error.NewRetryPolicy(4, 100, 1000)
    println policy.max_attempts, policy.base_delay_ms, policy.max_delay_ms
    e := error.New(error.KindTimeout, "the server did not answer")
    mut attempt := 1
    until error.ShouldRetry(policy, e, attempt):
        println "attempt", attempt, "after", error.DelayForAttempt(policy, attempt), "milliseconds"
        attempt += 1
    end
    println error.ShouldRetry(policy, error.New(error.KindInvalidArgument, "bad input"), 1)
end
Output4 100 1000 attempt 1 after 100 milliseconds attempt 2 after 200 milliseconds attempt 3 after 400 milliseconds false

Constants and variables

KindUnknown

const KindUnknown: str = "unknown"
An unspecified kind.

The example for this item is shown with the ones above: error-New.salam

KindNotFound

const KindNotFound: str = "not_found"
What was asked for was not found.

The example for this item is shown with the ones above: error-New.salam

KindInvalidArgument

const KindInvalidArgument: str = "invalid_argument"
The input is wrong; trying again will not help.

The example for this item is shown with the ones above: error-New.salam

KindPermissionDenied

const KindPermissionDenied: str = "permission_denied"
Access is not allowed.
error-Kinds.salam
import error

func main:
    each kind in [error.KindPermissionDenied, error.KindAlreadyExists, error.KindTimeout, error.KindCanceled, error.KindUnavailable, error.KindUnknown, error.KindUnimplemented]:
        e := error.New(kind, "an example")
        println kind, error.IsPermissionDenied(e), error.IsAlreadyExists(e), error.IsTimeout(e), error.IsCanceled(e), error.IsUnavailable(e), error.IsRetryable(e)
    end
end
Outputpermission_denied true false false false false false already_exists false true false false false false timeout false false true false false true canceled false false false true false false unavailable false false false false true true unknown false false false false false false unimplemented false false false false false false

KindAlreadyExists

const KindAlreadyExists: str = "already_exists"
It is already there.

The example for this item is shown with the ones above: error-Kinds.salam

KindUnavailable

const KindUnavailable: str = "unavailable"
The service is unavailable for now; trying again may work.

The example for this item is shown with the ones above: error-Kinds.salam

KindTimeout

const KindTimeout: str = "timeout"
Time ran out; trying again may work.

The example for this item is shown with the ones above: error-Kinds.salam

KindCanceled

const KindCanceled: str = "canceled"
The work was cancelled.

The example for this item is shown with the ones above: error-Kinds.salam

KindInternal

const KindInternal: str = "internal"
An internal fault in the program.
error-Wrap.salam
import error

func main:
    root := error.New(error.KindIO, "the disk is full")
    a := error.Wrap(root, "could not save the report")
    b := error.WrapAt(a, "the nightly run stopped", "nightly job")
    c := error.WrapKind(b, error.KindInternal, "internal error")
    d := error.WithMeta(c, "disk", "/dev/sda1")
    println error.Message(d)
    println error.RootMessage(d)
    println error.Kind(d), error.IsInternal(d), error.Meta(d, "disk")
    println error.Trace(d)
    println error.Format(d)
end
Outputinternal error: the nightly run stopped: could not save the report: the disk is full the disk is full internal true /dev/sda1 nightly job [internal] internal error: the nightly run stopped: could not save the report: the disk is full trace: nightly job disk=/dev/sda1

KindIO

const KindIO: str = "io"
A read or write error.

The example for this item is shown with the ones above: error-New.salam

KindUnimplemented

const KindUnimplemented: str = "unimplemented"
Not implemented yet.

The example for this item is shown with the ones above: error-Kinds.salam

Functions

Nil

func Nil(): Error
The nil error, meaning "no error".

The example for this item is shown with the ones above: error-New.salam

IsNil

func IsNil(e: Error): bool
True when the error is nil.

The example for this item is shown with the ones above: error-New.salam

Ok

func Ok(e: Error): bool
The same as IsNil, and easier to read in a condition.

The example for this item is shown with the ones above: error-New.salam

New

func New(kind: str, message: str): Error
A new error with a kind and a message.

The example for this item is shown with the ones above: error-New.salam

NewAt

func NewAt(kind: str, message: str, at: str): Error
A new error, with the name of the place it happened.

The example for this item is shown with the ones above: error-New.salam

Newf

func Newf(kind: str, tmpl: str, args: Vector<str>): Error
A new error with a formatted message; every {} is filled with the next value.

The example for this item is shown with the ones above: error-New.salam

Wrap

func Wrap(e: Error, message: str): Error
Puts a new message on an error and keeps the earlier ones; the kind does not change. When the error is nil it builds a new one of unknown kind.

The example for this item is shown with the ones above: error-Wrap.salam

WrapAt

func WrapAt(e: Error, message: str, at: str): Error
Like Wrap, with the name of the place.

The example for this item is shown with the ones above: error-Wrap.salam

WrapKind

func WrapKind(e: Error, kind: str, message: str): Error
Like Wrap, and changes the kind too; the earlier kind is still recognised by Is.

The example for this item is shown with the ones above: error-Wrap.salam

WithMeta

func WithMeta(e: Error, key: str, val: str): Error
Adds a key and value pair to the error, such as a user id or a file path.

The example for this item is shown with the ones above: error-Wrap.salam

Meta

func Meta(e: Error, key: str): str
The value of a metadata key, or an empty string when it is absent.

The example for this item is shown with the ones above: error-Wrap.salam

Kind

func Kind(e: Error): str
The kind of the error.

The example for this item is shown with the ones above: error-New.salam

Is

func Is(e: Error, kind: str): bool
True when the error's kind, or the kind of one of its causes, is this one.

The example for this item is shown with the ones above: error-New.salam

Message

func Message(e: Error): str
The full message with every layer.

The example for this item is shown with the ones above: error-New.salam

RootMessage

func RootMessage(e: Error): str
The message of the innermost cause.

The example for this item is shown with the ones above: error-Wrap.salam

Trace

func Trace(e: Error): str
The recorded places, joined with ->.

The example for this item is shown with the ones above: error-Wrap.salam

Format

func Format(e: Error): str
The full text for a log: the kind in brackets, the message, the places and the metadata, each on its own line.

The example for this item is shown with the ones above: error-Wrap.salam

IsNotFound

func IsNotFound(e: Error): bool
True when the error is of the "not found" kind.

The example for this item is shown with the ones above: error-New.salam

IsInvalidArgument

func IsInvalidArgument(e: Error): bool
For the "invalid argument" kind.

The example for this item is shown with the ones above: error-New.salam

IsPermissionDenied

func IsPermissionDenied(e: Error): bool
For the "permission denied" kind.

The example for this item is shown with the ones above: error-Kinds.salam

IsAlreadyExists

func IsAlreadyExists(e: Error): bool
For the "already exists" kind.

The example for this item is shown with the ones above: error-Kinds.salam

IsTimeout

func IsTimeout(e: Error): bool
For the "timeout" kind.

The example for this item is shown with the ones above: error-Kinds.salam

IsCanceled

func IsCanceled(e: Error): bool
For the "cancelled" kind.

The example for this item is shown with the ones above: error-Kinds.salam

IsUnavailable

func IsUnavailable(e: Error): bool
For the "unavailable" kind.

The example for this item is shown with the ones above: error-Kinds.salam

IsInternal

func IsInternal(e: Error): bool
For the "internal" kind.

The example for this item is shown with the ones above: error-Wrap.salam

IsRetryable

func IsRetryable(e: Error): bool
True when the error is transient and worth retrying: a timeout, unavailability or a read or write error.

The example for this item is shown with the ones above: error-Kinds.salam

NewRetryPolicy

func NewRetryPolicy(max_attempts: int, base_delay_ms: int, max_delay_ms: int): RetryPolicy
Builds a policy with a maximum number of attempts and a base and maximum delay.

The example for this item is shown with the ones above: error-RetryPolicy.salam

DelayForAttempt

func DelayForAttempt(p &: RetryPolicy, attempt: int): int
How long to wait after attempt number attempt failed: the base delay after the first, then doubling each time up to the ceiling.

The example for this item is shown with the ones above: error-RetryPolicy.salam

ShouldRetry

func ShouldRetry(p &: RetryPolicy, e: Error, attempt: int): bool
True when an error happened, is transient, and attempts are left.

The example for this item is shown with the ones above: error-RetryPolicy.salam

Free

func Free(e &: Error)
Releases the error's memory.

The example for this item is shown with the ones above: error-New.salam