Exceptions

Error handling with guard, raise, and exception types.

Note: Exception handling requires (import (sigil error)).

guard

Handle exceptions with pattern-like clauses. The primary way to catch errors.

(import (sigil error))

(guard (err
        ((type-error? err)
         (display "Type error: ")
         (display (exception-message err))
         #f)
        ((error? err)
         (display "Error occurred")
         #f)
        (else
         (raise err)))  ; Re-raise if unhandled
  (risky-operation))

Syntax

(guard (variable
        (test expression ...)
        (test expression ...)
        (else expression ...))
  body ...)

Each clause tests the caught exception. If a test succeeds, its expressions are evaluated and the result returned. The else clause catches anything.

Without else

If no clause matches and there's no else, the exception propagates.

(guard (e ((eq? (exception-kind e) 'expected) 'handled))
  (raise (make-exception 'unexpected "oops" '())))
; Exception propagates (not caught)

raise

Signal an exception. Transfers control to the nearest handler.

(raise (make-exception 'my-error "something went wrong" '()))

error

Signal an error with a message and optional irritants (R7RS). The message is used as written, and the irritants are kept on the exception (exception-irritants) and shown after the message when the error is reported.

(error "Something went wrong")
(error "Invalid argument:" x)
(error (format "Expected ~a, got ~a" expected actual))

error does not interpret ~a or ~s in its message. To put values inside the message text, build it with format first, as in the last example.

Exception Types

Sigil provides typed exceptions via (sigil error):

(import (sigil error))

;; Type predicates
(exception? e)     ; Any exception
(error? e)         ; General error
(type-error? e)    ; Type mismatch
(range-error? e)   ; Value out of range
(arity-error? e)   ; Wrong argument count

;; Low-level exception constructors
(make-exception 'kind "message" irritants)
(make-error "message")

;; Struct error constructors (preferred)
(type-error message: "msg" expected-type: "pair" procedure-name: "car")
(arity-error message: "msg" expected-count: 2 got-count: 3)
(range-error message: "msg" index: 10 min-bound: 0 max-bound: 5)
(unbound-error message: "msg" variable-name: "x")
(io-error message: "msg" path: "/tmp/foo" operation: "open")

Exception Fields

(exception-kind e)       ; Symbol identifying error type
(exception-message e)    ; Human-readable message
(exception-irritants e)  ; Related values (list)
(exception-stack-trace e) ; Stack trace if available

make-exception

Create a custom exception.

(make-exception 'not-found "Resource not found" (list path))

Arguments:

  1. Kind (symbol) - identifies the exception type
  2. Message (string) - human-readable description
  3. Irritants (list) - related values for debugging

Common Patterns

Safe Operations

(define (safe-divide a b)
  (guard (e (else #f))
    (/ a b)))

(safe-divide 10 2)   ; => 5
(safe-divide 10 0)   ; => #f

Default on Error

(define (read-config-or-default path)
  (guard (e ((error? e) (default-config)))
    (read-config path)))

Re-raise After Logging

(define (with-error-logging thunk)
  (guard (e (else
             (display "Error: ")
             (display (exception-message e))
             (newline)
             (raise e)))  ; Re-raise after logging
    (thunk)))

Stack Traces

When an unhandled exception reaches the REPL, Sigil prints a stack trace showing where the error occurred.

In code, access via (exception-stack-trace e) when available.

with-exception-handler

Low-level handler installation. Rarely needed directly—prefer guard.

(with-exception-handler
  (lambda (e)
    (display "caught!")
    (raise e))  ; Must re-raise or return
  (lambda ()
    (risky-operation)))

The handler must either:

  • Raise the exception (or a different one)
  • Return a value (only for continuable exceptions)