werr

package
v1.20.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotABoolean      = NewTypeSentinel("boolean")
	ErrNotAPort         = NewTypeSentinel("port")
	ErrNotAnInputPort   = NewTypeSentinel("input port")
	ErrNotAnOutputPort  = NewTypeSentinel("output port")
	ErrNotABox          = NewTypeSentinel("box")
	ErrNotAnOpaqueValue = NewTypeSentinel("opaque value")
	ErrNotAByte         = NewTypeSentinel("byte")
	// ErrNotAByteInputPort and ErrNotAByteOutputPort use "binary" to match the
	// user-facing terminology in R7RS and values/value_type.go; the var names
	// keep "Byte" for historical reasons but the message phrases track the
	// rest of the codebase.
	ErrNotAByteInputPort          = NewTypeSentinel("binary input port")
	ErrNotAByteOutputPort         = NewTypeSentinel("binary output port")
	ErrNotATextualPort            = NewTypeSentinel("textual port")
	ErrNoIOState                  = NewStaticError("no io state")
	ErrStopIteration              = NewStaticError("stop iteration")
	ErrNotAPrimitive              = NewTypeSentinel("primitive")
	ErrNoSuchBinding              = NewStaticError("no such binding")
	ErrAmbiguousBinding           = NewStaticError("ambiguous binding")
	ErrSyntacticKeywordAsVariable = NewStaticError("syntactic keyword used as variable")
	ErrNotANumber                 = NewTypeSentinel("number")
	ErrCannotCompare              = NewStaticError("cannot compare values")
	ErrNotAReal                   = NewTypeSentinel("real number")
	ErrDivisionByZero             = NewStaticError("division by zero")
	ErrNotAList                   = NewTypeSentinel("list")
	ErrCircularList               = NewStaticError("circular list")
	ErrNotACloseParen             = NewStaticError("not a close parenthesis")
	ErrDatumLabelUndefined        = NewStaticError("undefined datum label")
	ErrWrongNumberOfArguments     = NewStaticError("wrong number of arguments")
	ErrWrongNumberOfValues        = NewStaticError("wrong number of values")
	ErrNotAMachineContext         = NewTypeSentinel("machine context")
	ErrNotAPair                   = NewTypeSentinel("pair")
	ErrNotACons                   = NewTypeSentinel("cons")
	ErrNotACharacter              = NewTypeSentinel("character")
	ErrNotACharSet                = NewTypeSentinel("char-set")
	ErrStackUnderflow             = NewStaticError("stack underflow")
	ErrStackOverflow              = NewStaticError("stack overflow")
	ErrNotASyntaxValue            = NewTypeSentinel("syntax value")
	ErrNotASyntaxPair             = NewTypeSentinel("syntax pair")
	ErrNotASyntaxSymbol           = NewTypeSentinel("syntax symbol")
	ErrNotASyntaxList             = NewTypeSentinel("syntax list")
	ErrNotASyntaxObject           = NewTypeSentinel("syntax object")
	ErrNotASymbol                 = NewTypeSentinel("symbol")
	ErrInvalidSyntax              = NewStaticError("invalid syntax")
	// ErrIncompleteInput marks input that is a valid prefix of an expression but
	// ended before completing (unterminated string/symbol, source ending inside an
	// unclosed form). REPLs errors.Is this to decide whether to read more input.
	// Lives in werr (not internal/parser) so the tokenizer can reference it without
	// an import cycle.
	ErrIncompleteInput           = NewStaticError("incomplete input")
	ErrInvalidArgument           = NewStaticError("invalid argument")
	ErrInternal                  = NewStaticError("internal error")
	ErrDuplicateBinding          = NewStaticError("duplicate binding")
	ErrNotAClosure               = NewTypeSentinel("closure")
	ErrUnknownCharacterMnemonic  = NewStaticError("unknown character mnemonic")
	ErrNotAnInteger              = NewTypeSentinel("integer")
	ErrNotALocalEnvironmentFrame = NewTypeSentinel("local environment frame")
	ErrNotAMachineTemplate       = NewTypeSentinel("machine template")
	ErrUnexpectedNil             = NewStaticError("unexpected nil value")
	ErrUnexpectedTransformer     = NewStaticError("unexpected transformer")
	ErrNotAString                = NewTypeSentinel("string")
	ErrNotANamespace             = NewTypeSentinel("namespace")
	ErrNotAVector                = NewTypeSentinel("vector")
	ErrNotAByteVector            = NewTypeSentinel("bytevector")
	ErrNotAProcedure             = NewTypeSentinel("procedure")
	ErrNotAParameter             = NewTypeSentinel("parameter")
	// ErrComposableContinuationFromGo marks an attempt to invoke a composable
	// continuation from Go via Engine.Call; composable continuations require the
	// VM winding stack and have no Go-callable form.
	ErrComposableContinuationFromGo = NewStaticError("cannot call composable continuation from Go")
	ErrNotAStringOutputPort         = NewTypeSentinel("string output port")
	ErrNotABytevectorOutputPort     = NewTypeSentinel("bytevector output port")
	ErrNotANativeError              = NewTypeSentinel("error object")
	ErrNotARecord                   = NewTypeSentinel("record")
	ErrNotARecordType               = NewTypeSentinel("record type")
	ErrOpaqueRecord                 = NewStaticError("record type is opaque")
	ErrFileNotFound                 = NewStaticError("file not found")
	ErrFileOpen                     = NewStaticError("file open failed")
	// ErrFileStat marks a failure to PROBE a path — a stat that neither found the
	// file nor established that it is absent (EACCES on a parent directory, say).
	// It is deliberately distinct from ErrFileOpen: no descriptor was requested,
	// so a caller matching ErrFileOpen to tell a failed open from a failed probe
	// must not see one reported as the other.
	ErrFileStat             = NewStaticError("file stat failed")
	ErrCircularDependency   = NewStaticError("circular library dependency")
	ErrUnexportedIdentifier = NewStaticError("identifier not exported")

	ErrNotAThread             = NewTypeSentinel("thread")
	ErrNotAMutex              = NewTypeSentinel("mutex")
	ErrNotAConditionVariable  = NewTypeSentinel("condition variable")
	ErrNotAnUncaughtException = NewTypeSentinel("uncaught-exception")
	ErrNotATime               = NewTypeSentinel("time")
	ErrNotAnAtomic            = NewTypeSentinel("atomic")
	ErrPortClosed             = NewStaticError("port is closed")
	ErrNotAHashtable          = NewTypeSentinel("hashtable")
	// ErrImmutableHashtable is raised by hashtable-set!, hashtable-delete! and
	// hashtable-clear! on a table that hashtable-copy produced without a true
	// mutable argument. R6RS raises &assertion here; Wile has no R6RS condition
	// system, so this sentinel is the mapping — same trigger, Wile's error model,
	// matchable with errors.Is from Go and by the standard exception machinery
	// from Scheme.
	ErrImmutableHashtable = NewStaticError("hashtable is immutable")
	// ErrUnsupportedHashtableKind is raised by make-hashtable for any
	// hash/equivalence pair other than the built-in (equal-hash, equal?).
	// User-supplied procedures are the deferred phase of the R6RS hashtable
	// work; see docs/reference/r7rs-differences.md.
	ErrUnsupportedHashtableKind = NewStaticError("unsupported hashtable hash/equivalence pair")
	ErrNoCaptureContext         = NewStaticError("no capture context for expansion")
	ErrExactnessConversion      = NewStaticError("exactness conversion failed")
	ErrLossyConversion          = NewStaticError("lossy conversion")
	ErrNumericRegistry          = NewStaticError("numeric registry violation")
	ErrInvalidFormat            = NewStaticError("invalid number format")
	ErrUnknownOpCode            = NewStaticError("unknown op code")
	ErrNotAMatch                = NewTypeSentinel("match")
	ErrNotAPromptTag            = NewTypeSentinel("prompt tag")
	ErrNotAContinuationMarkSet  = NewTypeSentinel("continuation mark set")
	ErrNotAContinuation         = NewTypeSentinel("continuation")
	ErrNotAnErrorContext        = NewTypeSentinel("error context")
	ErrTypeConversion           = NewStaticError("type conversion failed")
	ErrCodepointOutOfRange      = NewStaticError("codepoint out of range")
	ErrIndexOutOfRange          = NewStaticError("index out of range")
	ErrImmutableString          = NewStaticError("cannot mutate immutable string")
	ErrImmutableVector          = NewStaticError("cannot mutate immutable vector")
	ErrImmutableBytevector      = NewStaticError("cannot mutate immutable bytevector")
	ErrImmutableBinding         = NewStaticError("cannot mutate immutable binding")
	// ErrImmutableParameterBase is raised when a parameter object whose base
	// value is shared beyond one Engine is applied to one argument.
	ErrImmutableParameterBase = NewStaticError("cannot mutate immutable parameter base")
	ErrInvariantViolation     = NewStaticError("invariant violation")

	ErrFFIRegistration          = NewStaticError("FFI registration error")
	ErrFFICallbackError         = NewStaticError("FFI callback error")
	ErrCallbackResultConversion = NewStaticError("callback result conversion failed")

	ErrEngineInit = NewStaticError("engine initialization error")

	ErrExpansion = NewStaticError("expansion error")

	ErrMissingPhaseRegistry = NewStaticError("missing PhaseRegistry")
	ErrNilParentEnvironment = NewStaticError("nil parent environment")

	ErrThreadPanic   = NewStaticError("thread panic")
	ErrPanicRecovery = NewStaticError("panic recovery")

	// ErrNotImplemented marks a primitive that is registered and callable but has
	// no working implementation behind it. It is deliberately distinct from a
	// runtime failure sentinel: the operation did not fail on its inputs, it does
	// not exist yet, and no input would make it succeed. Callers matching on it
	// are asking "is this feature wired?", not "did this call go wrong?".
	ErrNotImplemented = NewStaticError("not implemented")

	ErrCannotDoubleSyntaxWrap  = NewStaticError("cannot wrap syntax value in SyntaxObject")
	ErrNoMatchingClause        = NewStaticError("no matching clause")
	ErrLibraryConfiguration    = NewStaticError("library configuration error")
	ErrLibraryFormMalformed    = NewStaticError("malformed library form")
	ErrLibraryNameMismatch     = NewStaticError("library name mismatch")
	ErrAllocationLimitExceeded = NewStaticError("allocation limit exceeded")
	ErrNonContinuableException = NewStaticError("non-continuable exception")

	ErrCallDepthExceeded   = NewStaticError("call depth exceeded")
	ErrParseDepthExceeded  = NewStaticError("parse nesting depth exceeded")
	ErrExpandDepthExceeded = NewStaticError("macro expansion nesting depth exceeded")
	ErrWriteDepthExceeded  = NewStaticError("write nesting depth exceeded")

	ErrExpiredEscape       = NewStaticError("expired escape procedure")
	ErrContinuationBarrier = NewStaticError("continuation barrier violation")

	ErrRandomGenerationFailed = NewStaticError("random generation failed")
	ErrInvalidLoadPath        = NewStaticError("invalid load path")

	// ErrOperationCancelled is for a thread unparked by ctx cancellation before a
	// blocking operation completed. Two producers: thread-sleep!, which returns
	// Void, and Thread.Join, which returns the joinee's result. Neither has a
	// value channel free for "did not finish" — exactly the shape of the removed
	// rw-mutex lock family that was this sentinel's original producer.
	// Primitives that DO have one use it instead — mutex-lock! and
	// (mutex-unlock! m cv) both report a cancelled wait as #f, error-free, which
	// is also what lets a wrapping with-timeout handler run
	// (docs/concurrency/cancellation.md). Raise this only where that convention
	// cannot be borrowed, and pair it with the raw ctx cause so a host can still
	// distinguish context.Canceled from context.DeadlineExceeded.
	ErrOperationCancelled = NewStaticError("synchronization operation cancelled")

	ErrNotAProcess = NewTypeSentinel("process")

	ErrJoinTimeout             = NewStaticError("thread-join!: timeout")
	ErrThreadAlreadyStarted    = NewStaticError("thread-start!: thread already started")
	ErrCrossThreadContinuation = NewStaticError("cannot invoke continuation from different thread")
)

Standard error values for type checking and runtime errors.

Functions

func FailureMessage added in v1.20.0

func FailureMessage(err error) string

FailureMessage renders err's chain with its category sentinels omitted: every wrap's own contextual message and every root cause, and nothing that exists only for errors.Is to match. It returns "" when the chain is nothing BUT a sentinel, which is the one case where a caller has to fall back to Error().

A rendered chain fuses two different things, and only the first is a message in R7RS §6.11's sense:

open-input-file: /nope: open /nope: no such file or directory   the FAILURE
file does not exist                                            its CATEGORY

The category is duplicated information — the same fact errors.Is answers structurally — and it reads as a non-sequitur appended to a complete sentence. Everything else stays. In particular a root cause stays: an OS error arrives in the same slot a sentinel would occupy (WrapForeignFileError), so stripping by slot rather than by type would delete "no such file or directory", the only substantive fact a file error carries.

It is also why this strips only the category and does NOT reduce to the innermost wrap. Every wrap message is supposed to name where and what, so at a primitive the outer ones are load-bearing: "file-exists?: argument 0" is the operation, not a breadcrumb. A caller that also wants to drop traversal breadcrumbs has to cut the chain on a boundary it can recognise before calling this — see machine.failureChain, which cuts at the compiler's provenance stamp.

Nothing is lost either way. This is a projection for the one accessor R7RS specifies; the caller still holds the whole error and renders it as before.

func RecoverAsError added in v1.19.0

func RecoverAsError(r any, sentinel error, site string) error

RecoverAsError normalizes the value returned by recover() into an error carrying the caller's sentinel.

A nil r (no panic in flight) yields a nil error, so a deferred closure may assign the result unconditionally. Every other value acquires the sentinel: an r that is already an error is chained as the cause, and any other value (panic("...") / panic(42)) has its text preserved in the message.

The sentinel must reach an error-typed r, not just a string one. A Go runtime fault inside foreign code — nil deref, index out of range, nil-map write — arrives here as a runtime.Error, which satisfies error. Returning it unchanged would strip the site's sentinel from precisely the panics that indicate host memory corruption, leaving them indistinguishable at the VM boundary from a deliberate (error "...") raised by a well-behaved primitive.

Chaining as a cause rather than returning unchanged does not disturb callers that route on the panic's identity: ForeignError.Is/As traverse the cause, so the VM's foreign-call bridge still matches prompt-abort, exception-escape, timer-interrupt, and continuation-resume signals through the wrap. No site type-asserts those signals directly.

The sentinel is a parameter rather than a fixed ErrInternal because each recover site's sentinel is part of that site's contract: the VM boundary reports ErrInternal, a thread root reports ErrThreadPanic, a foreign call reports ErrPanicRecovery.

site names the recovering site and prefixes the message, e.g. "thread \"w\"" or "foreign function call". Pass "" when the caller wraps the result again with its own prefix, so the site name does not appear twice.

func TypeNameOf

func TypeNameOf(err error) string

TypeNameOf extracts the expected-type phrase from any error wrapping a type-mismatch sentinel. Returns "" for non-sentinel errors or sentinels without a type name. Use this in helpers that format error messages from a sentinel parameter — it works regardless of where the helper lives in the dependency graph.

Types

type ForeignError

type ForeignError struct {
	// contains filtered or unexported fields
}

ForeignError is an error type for Go primitive implementations (functions foreign to Scheme). It wraps an optional underlying error with a message.

func NewForeignErrorf

func NewForeignErrorf(msg string, vs ...any) *ForeignError

NewForeignErrorf creates a new foreign error with a formatted message.

func WrapForeignErrorWithCause

func WrapForeignErrorWithCause(sentinel, cause error, msg string, vs ...any) *ForeignError

WrapForeignErrorWithCause wraps a sentinel and a root cause into a single ForeignError. The sentinel is matched by errors.Is for programmatic dispatch; the cause preserves the underlying failure for diagnostics.

func WrapForeignErrorf

func WrapForeignErrorf(err error, msg string, vs ...any) *ForeignError

WrapForeignErrorf wraps an existing error with a formatted message.

func (*ForeignError) As

func (p *ForeignError) As(target any) bool

As checks whether the sentinel or cause can be assigned to target.

func (*ForeignError) Cause

func (p *ForeignError) Cause() error

Cause returns the root cause error, if any. Useful for debugging/logging when you need the underlying failure directly.

func (*ForeignError) Error

func (p *ForeignError) Error() string

func (*ForeignError) Is

func (p *ForeignError) Is(target error) bool

Is reports whether target matches the sentinel or the cause. This replaces Unwrap and gives ForeignError precise two-chain semantics: the sentinel identifies the error category, the cause preserves the root failure from the underlying operation.

type ForeignFileError

type ForeignFileError struct {
	ForeignError
	Filename string // the file path that caused the error
	Op       string // the operation (e.g., "open-input-file", "delete-file")
}

ForeignFileError represents an error from a file system operation. R7RS §6.11: detected by file-error? predicate. The embedded ForeignError is a VALUE, not a pointer, and that is load-bearing: every one of its methods (Error, Is, As, Cause) has a pointer receiver, so embedding by value keeps them out of the method set of ForeignFileError and puts them only in *ForeignFileError's. Embedding a *ForeignError instead promotes them to BOTH, leaving two types that satisfy error for one error. go vet's printf check rejects that outright ("%w wants operand of error type werr.ForeignFileError, not pointer type *werr.ForeignFileError (defeats errors.Is)"), and it is right to: an errors.As target of the value type would silently never match. The same applies to ForeignProcessError and ForeignReadError below.

func WrapForeignFileError

func WrapForeignFileError(err error, op string, filename string) *ForeignFileError

WrapForeignFileError wraps an OS error with file context.

type ForeignProcessError

type ForeignProcessError struct {
	ForeignError
	Command string // the command that was run
	Op      string // the operation (e.g., "process-spawn", "system")
}

ForeignProcessError represents an error from a process operation. Parallel to ForeignFileError for programmatic inspection of failed process operations.

func WrapForeignProcessError

func WrapForeignProcessError(err error, op string, command string) *ForeignProcessError

WrapForeignProcessError wraps an OS error with process context.

type ForeignReadError

type ForeignReadError struct {
	ForeignError
}

ForeignReadError represents an error from a read or parse operation. R7RS §6.11: detected by read-error? predicate.

func NewForeignReadErrorf

func NewForeignReadErrorf(msg string, vs ...any) *ForeignReadError

NewForeignReadErrorf creates a new read error with a formatted message.

func WrapForeignReadErrorf

func WrapForeignReadErrorf(err error, msg string, vs ...any) *ForeignReadError

WrapForeignReadErrorf wraps an error as a read error.

type StaticError

type StaticError struct {
	// contains filtered or unexported fields
}

StaticError is a sentinel error type for programmatic matching via errors.Is. Each sentinel carries a fixed human-readable message and serves as a stable identity that callers can match across error wrapping layers.

Type-mismatch sentinels (declared via NewTypeSentinel) additionally carry an expectedType phrase (e.g., "a string") used by argument-extraction helpers in registry/helpers to format error messages without each call site repeating the phrase.

func NewStaticError

func NewStaticError(msg string) *StaticError

NewStaticError creates a new static error with the given message. The returned sentinel has no expectedType; callers using it with type-checking helpers will get a degraded ("expected but got ...") error message. Use NewTypeSentinel for type-mismatch sentinels.

func NewTypeSentinel

func NewTypeSentinel(noun string) *StaticError

NewTypeSentinel creates a sentinel for a type-mismatch error. The noun argument is the bare type-name ("string", "integer", "char-set", …); NewTypeSentinel auto-prefixes "a"/"an" using articleFor. The sentinel's Error() value is "not " + article + " " + noun, e.g. "not a string" or "not an integer".

For irregular cases where letter-based article selection produces the wrong result (e.g., "once" pronounced /wuns/ wants "a", not "an"), pass the already-articled phrase: NewTypeSentinel("a once"). Inputs starting with "a " or "an " are used verbatim.

Panics on empty noun — a type sentinel without a noun is a programmer bug, since the resulting "expected " phrase would be useless. Use NewStaticError for sentinels that aren't type-mismatch errors.

func (*StaticError) Error

func (p *StaticError) Error() string

func (*StaticError) TypeName

func (p *StaticError) TypeName() string

TypeName returns the expected-type phrase for type-mismatch sentinels (those constructed via NewTypeSentinel). Returns "" for sentinels constructed via NewStaticError.

Jump to

Keyboard shortcuts

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