9.2 Exceptions
Exceptions in The Racket Guide introduces exceptions.
See Exceptions for information on the Racket exception model. It is based on a proposal by Friedman, Haynes, and Dybvig [Friedman95].
Whenever a primitive error occurs in Racket, an exception is raised. The value that is passed to the current exception handler for a primitive error is always an instance of the exn structure type. Every exn structure value has a message field that is a string, the primitive error message. The default exception handler recognizes exception values with the exn? predicate and passes the error message to the current error display handler (see error-display-handler).
Primitive procedures that accept a procedure argument with a particular required arity (e.g., call-with-input-file, call/cc) check the argument’s arity immediately, raising exn:fail:contract if the arity is incorrect.
9.2.1 Error Message Conventions
Racket’s error message convention is to produce error messages with the following shape:
‹srcloc›: ‹name›: ‹message›; ‹continued-message› ... ‹field›: ‹detail› ...
The message starts with an optional source location, ‹srcloc›, which is followed by a colon and space when present. The message continues with an optional ‹name› that usually identifies the complaining function, syntactic form, or other entity, but may also refer to an entity being complained about; the ‹name› is also followed by a colon and space when present.
The ‹message› should be relatively short, and it should be
largely independent of specific values that triggered the error. More
detailed explanation that requires multiple lines should continue with
each line indented by a single space, in which case ‹message›
should end in a semi-colon (but the semi-colon should be omitted if
‹continued-message› is not present). Message text should be
lowercase—
Specific values that triggered the error or other helpful information should appear in separate ‹field› lines, each of which is indented by two spaces. If a ‹detail› is especially long or takes multiple lines, it should start on its own line after the ‹field› label, and each of its lines should be indented by three spaces. Field names should be all lowercase.
A ‹field› name should end with ... if the field provides relatively detailed information that might be distracting in common cases but useful in others. For example, when a contract failure is reported for a particular argument of a function, other arguments to the function might be shown in an “other arguments...” field. The intent is that fields whose names end in ... might be hidden by default in an environment such as DrRacket.
Make ‹field› names as short as possible, relying on ‹message› or ‹continued message› text to clarify the meaning for a field. For example, prefer “given” to “given turtle” as a field name, where ‹message› is something like “given turtle is too sleepy” to clarify that “given” refers to a turtle.
9.2.2 Raising Exceptions
If barrier? is true, then the call to the exception handler is protected by a continuation barrier, so that multiple returns/escapes are impossible. All exceptions raised by racket functions effectively use raise with a #t value for barrier?.
Breaks are disabled from the time the exception is raised until the exception handler obtains control, and the handler itself is parameterize-breaked to disable breaks initially; see Breaks for more information on breaks.
Examples: | |||||||||||||||||
|
procedure
sym : symbol? (error msg v ...) → any msg : string? v : any/c (error src format v ...) → any src : symbol? format : string? v : any/c
(error sym) creates a message string by concatenating "error: " with the string form of sym. Use this form sparingly.
(error msg v ...) creates a message string by concatenating msg with string versions of the vs (as produced by the current error value conversion handler; see error-value->string-handler). A space is inserted before each v. Use this form sparingly, because it does not conform well to Racket’s error message conventions; consider raise-arguments-error, instead.
(error src frmat v ...) creates a message string equivalent to the string created by
(format (string-append "~s: " frmat) src v ...)
When possible, use functions such as raise-argument-error, instead, which construct messages that follow Racket’s error message conventions.
In all cases, the constructed message string is passed to make-exn:fail, and the resulting exception is raised.
Examples: | ||||||
|
procedure
(raise-user-error sym) → any
sym : symbol? (raise-user-error msg v ...) → any msg : string? v : any/c (raise-user-error src format v ...) → any src : symbol? format : string? v : any/c
procedure
(raise-argument-error name expected v) → any
name : symbol? expected : string? v : any/c
(raise-argument-error name expected bad-pos v ...) → any name : symbol? expected : string? bad-pos : exact-nonnegative-integer? v : any/c
In the first form, v is the value received by the procedure that does not have the expected type.
In the second form, the bad argument is indicated by an index bad-pos (counting from 0), and all of the original arguments v are provided (in order). The resulting error message names the bad argument and also lists the other arguments. If bad-pos is not less than the number of vs, the exn:fail:contract exception is raised.
Examples: | ||||||||||||||||||||||||||||||||||||||
|
procedure
(raise-result-error name expected v) → any
name : symbol? expected : string? v : any/c
(raise-result-error name expected bad-pos v ...) → any name : symbol? expected : string? bad-pos : exact-nonnegative-integer? v : any/c
procedure
(raise-arguments-error name message field v ... ...) → any name : symbol? message : string? field : string? v : any/c
Example: | ||||||||
|
procedure
(raise-range-error name type-description index-prefix index in-value lower-bound upper-bound alt-lower-bound) → any name : symbol? type-description : string? index-prefix : string? index : exact-integer? in-value : any/c lower-bound : exact-integer? upper-bound : exact-integer? alt-lower-bound : (or/c #f exact-integer?)
Since upper-bound is inclusive, a typical value is one
less than the size of a collection—
Examples: | |||||||||||||||||||
|
procedure
(raise-type-error name expected v) → any
name : symbol? expected : string? v : any/c (raise-type-error name expected bad-pos v ...) → any name : symbol? expected : string? bad-pos : exact-nonnegative-integer? v : any/c
procedure
(raise-mismatch-error name message v ...+ ...+) → any name : symbol? message : string? v : any/c
procedure
(raise-arity-error name arity-v [arg-v ...]) → any
name : (or/c symbol? procedure?)
arity-v :
(or/c exact-nonnegative-integer? arity-at-least? (listof (or/c exact-nonnegative-integer? arity-at-least?))) arg-v : any/c = #f
The arity-v value must be a possible result from procedure-arity, except that it does not have to be normalized (see procedure-arity? for the details of normalized arities); raise-arity-error will normalize the arity and use the normalized form in the error message. If name is a procedure, its actual arity is ignored.
The arg-v arguments are the actual supplied arguments, which are shown in the error message (using the error value conversion handler; see error-value->string-handler); also, the number of supplied arg-vs is explicitly mentioned in the message.
procedure
(raise-syntax-error name message [ expr sub-expr extra-sources]) → any name : (or/c symbol? #f) message : string? expr : any/c = #f sub-expr : any/c = #f extra-sources : (listof syntax?) = null
The name argument is usually #f when expr is provided; it is described in more detail below. The message is used as the main body of the error message; if message contains newline characters, each new line should be suitably indented (with one space at the start), and it should not end with a newline character.
The optional expr argument is the erroneous source syntax object or S-expression (but the expression #f cannot be represented by itself; it must be wrapped as a syntax object). The optional sub-expr argument is a syntax object or S-expression (again, #f cannot represent itself) within expr that more precisely locates the error. Both may appear in the generated error-message text if error-print-source-location is #t. Source location information in the error-message text is similarly extracted from sub-expr or expr when at least one is a syntax object and error-print-source-location is #t.
If sub-expr is provided and not #f, it is used (in syntax form) for the exprs field of the generated exception record, else the expr is used if provided and not #f. In either case, the syntax object is consed onto extra-sources to produce the exprs field, or extra-sources is used directly for exprs if neither expr nor sub-expr is provided and not #f.
The form name used in the generated error message is determined through a combination of the name, expr, and sub-expr arguments:
When name is #f, and when expr is either an identifier or a syntax pair containing an identifier as its first element, then the form name from the error message is the identifier’s symbol.
When name is #f and when expr is not an identifier or a syntax pair containing an identifier as its first element, then the form name in the error message is "?".
symbol: When name is a symbol, then the symbol is used as the form name in the generated error message.
9.2.3 Handling Exceptions
Any procedure that takes one argument can be an exception handler.
Normally, an exception handler escapes from the context of the
raise call via abort-current-continuation or some other escape
mechanism. To propagate an exception to the “previous” exception
handler—
A call to an exception handler is parameterize-breaked to disable breaks, and it is wrapped with call-with-exception-handler to install an exception handler that reports both the original and newly raised exceptions via the error display handler and then escapes via the error escape handler.
parameter
(uncaught-exception-handler) → (any/c . -> . any)
(uncaught-exception-handler f) → void? f : (any/c . -> . any)
The default uncaught-exception handler prints an error message using the current error display handler (see error-display-handler), unless the argument to the handler is an instance of exn:break:hang-up. If the argument to the handler is an instance of exn:break:hang-up or exn:break:terminate, the default uncaught-exception handler then calls the exit handler with 1, which normally exits or escapes. For any argument, the default uncaught-exception handler then escapes by calling the current error escape handler (see error-escape-handler). The call to each handler is parameterized to set error-display-handler to the default error display handler, and it is parameterize-breaked to disable breaks. The call to the error escape handler is further parameterized to set error-escape-handler to the default error escape handler; if the error escape handler returns, then the default error escape handler is called.
When the current error display handler is the default handler, then the error-display call is parameterized to install an emergency error display handler that logs an error (see log-error) and never fails.
syntax
(with-handlers ([pred-expr handler-expr] ...) body ...+)
The new exception handler processes an exception only if one of the pred-expr procedures returns a true value when applied to the exception, otherwise the exception handler is invoked from the continuation of the with-handlers expression (by raising the exception again). If an exception is handled by one of the handler-expr procedures, the result of the entire with-handlers expression is the return value of the handler.
When an exception is raised during the evaluation of bodys, each predicate procedure pred-expr is applied to the exception value; if a predicate returns a true value, the corresponding handler-expr procedure is invoked with the exception as an argument. The predicates are tried in the order that they are specified.
Before any predicate or handler procedure is invoked, the continuation of the entire with-handlers expression is restored, but also parameterize-breaked to disable breaks. Thus, breaks are disabled by default during the predicate and handler procedures (see Breaks), and the exception handler is the one from the continuation of the with-handlers expression.
The exn:fail? procedure is useful as a handler predicate to catch all error exceptions. Avoid using (lambda (x) #t) as a predicate, because the exn:break exception typically should not be caught (unless it will be re-raised to cooperatively break). Beware, also, of catching and discarding exceptions, because discarding an error message can make debugging unnecessarily difficult; instead of discarding an error message, consider logging it via log-error or a logging form created by define-logger.
Examples: | ||||||||||||||||
|
syntax
(with-handlers* ([pred-expr handler-expr] ...) body ...+)
9.2.4 Configuring Default Handling
parameter
(error-escape-handler) → (-> any)
(error-escape-handler proc) → void? proc : (-> any)
The error escape handler is normally called directly by an exception handler, in a parameterization that sets the error display handler and error escape handler to the default handlers, and it is normally parameterize-breaked to disable breaks. To escape from a run-time error in a different context, use raise or error.
Due to a continuation barrier around exception-handling calls, an error escape handler cannot invoke a full continuation that was created prior to the exception, but it can abort to a prompt (see call-with-continuation-prompt) or invoke an escape continuation (see call-with-escape-continuation).
parameter
(error-display-handler) → (string? any/c . -> . any)
(error-display-handler proc) → void? proc : (string? any/c . -> . any)
The default error display handler displays its first argument to the current error port (determined by the current-error-port parameter) and extracts a stack trace (see continuation-mark-set->context) to display from the second argument if it is an exn value but not an exn:fail:user value.
The default error display handler in DrRacket also uses the second argument to highlight source locations.
To report a run-time error, use raise or procedures like error, instead of calling the error display handler directly.
parameter
(error-print-width) → (and/c exact-integer? (>=/c 3))
(error-print-width width) → void? width : (and/c exact-integer? (>=/c 3))
parameter
(error-print-context-length cnt) → void? cnt : exact-nonnegative-integer?
parameter
→
(any/c exact-nonnegative-integer? . -> . string?) (error-value->string-handler proc) → void?
proc :
(any/c exact-nonnegative-integer? . -> . string?)
The integer argument to the handler specifies the maximum number of characters that should be used to represent the value in the resulting string. The default error value conversion handler prints the value into a string (using the current global port print handler; see global-port-print-handler). If the printed form is too long, the printed form is truncated and the last three characters of the return string are set to “...”.
If the string returned by an error value conversion handler is longer than requested, the string is destructively “truncated” by setting the first extra position in the string to the null character. If a non-string is returned, then the string "..." is used. If a primitive error string needs to be generated before the handler has returned, the default error value conversion handler is used.
Calls to an error value conversion handler are parameterized to re-install the default error value conversion handler, and to enable printing of unreadable values (see print-unreadable).
parameter
(error-print-source-location include?) → void? include? : any/c
9.2.5 Built-in Exception Types
struct
(struct exn (message continuation-marks) #:extra-constructor-name make-exn #:transparent) message : string? continuation-marks : continuation-mark-set?
Exceptions raised by Racket form a hierarchy under exn:
struct
(struct exn:fail exn () #:extra-constructor-name make-exn:fail #:transparent)
struct
(struct exn:fail:contract exn:fail () #:extra-constructor-name make-exn:fail:contract #:transparent)
struct
(struct exn:fail:contract:arity exn:fail:contract () #:extra-constructor-name make-exn:fail:contract:arity #:transparent)
struct
(struct exn:fail:contract:divide-by-zero exn:fail:contract () #:extra-constructor-name make-exn:fail:contract:divide-by-zero #:transparent)
struct
(struct exn:fail:contract:non-fixnum-result exn:fail:contract () #:extra-constructor-name make-exn:fail:contract:non-fixnum-result #:transparent)
struct
(struct exn:fail:contract:continuation exn:fail:contract () #:extra-constructor-name make-exn:fail:contract:continuation #:transparent)
struct
(struct exn:fail:contract:variable exn:fail:contract (id) #:extra-constructor-name make-exn:fail:contract:variable #:transparent) id : symbol?
struct
(struct exn:fail:syntax exn:fail (exprs) #:extra-constructor-name make-exn:fail:syntax #:transparent) exprs : (listof syntax?)
struct
(struct exn:fail:syntax:unbound exn:fail:syntax () #:extra-constructor-name make-exn:fail:syntax:unbound #:transparent)
struct
(struct exn:fail:read exn:fail (srclocs) #:extra-constructor-name make-exn:fail:read #:transparent) srclocs : (listof srcloc?)
struct
(struct exn:fail:read:eof exn:fail:read () #:extra-constructor-name make-exn:fail:read:eof #:transparent)
struct
(struct exn:fail:read:non-char exn:fail:read () #:extra-constructor-name make-exn:fail:read:non-char #:transparent)
struct
(struct exn:fail:filesystem exn:fail () #:extra-constructor-name make-exn:fail:filesystem #:transparent)
struct
(struct exn:fail:filesystem:exists exn:fail:filesystem () #:extra-constructor-name make-exn:fail:filesystem:exists #:transparent)
struct
(struct exn:fail:filesystem:version exn:fail:filesystem () #:extra-constructor-name make-exn:fail:filesystem:version #:transparent)
struct
(struct exn:fail:filesystem:errno exn:fail:filesystem (errno) #:extra-constructor-name make-exn:fail:filesystem:errno #:transparent) errno : (cons/c exact-integer? (or/c 'posix 'windows 'gai))
struct
(struct exn:fail:network exn:fail () #:extra-constructor-name make-exn:fail:network #:transparent)
struct
(struct exn:fail:network:errno exn:fail:network (errno) #:extra-constructor-name make-exn:fail:network:errno #:transparent) errno : (cons/c exact-integer? (or/c 'posix 'windows 'gai))
struct
(struct exn:fail:out-of-memory exn:fail () #:extra-constructor-name make-exn:fail:out-of-memory #:transparent)
struct
(struct exn:fail:unsupported exn:fail () #:extra-constructor-name make-exn:fail:unsupported #:transparent)
struct
(struct exn:fail:user exn:fail () #:extra-constructor-name make-exn:fail:user #:transparent)
struct
(struct exn:break exn (continuation) #:extra-constructor-name make-exn:break #:transparent) continuation : continuation?
struct
(struct exn:break:hang-up exn:break () #:extra-constructor-name make-exn:break:hang-up #:transparent)
struct
(struct exn:break:terminate exn:break () #:extra-constructor-name make-exn:break:terminate #:transparent)
The property value must be a procedure that accepts a single
value—
#lang racket ;; We create a structure that supports the ;; prop:exn:srcloc protocol. It carries ;; with it the location of the syntax that ;; is guilty. (define-struct (exn:fail:he-who-shall-not-be-named exn:fail) (a-srcloc) #:property prop:exn:srclocs (lambda (a-struct) (match a-struct [(struct exn:fail:he-who-shall-not-be-named (msg marks a-srcloc)) (list a-srcloc)]))) ;; We can play with this by creating a form that ;; looks at identifiers, and only flags specific ones. (define-syntax (skeeterize stx) (syntax-case stx () [(_ expr) (cond [(and (identifier? #'expr) (eq? (syntax-e #'expr) 'voldemort)) (quasisyntax/loc stx (raise (make-exn:fail:he-who-shall-not-be-named "oh dear don't say his name" (current-continuation-marks) (srcloc '#,(syntax-source #'expr) '#,(syntax-line #'expr) '#,(syntax-column #'expr) '#,(syntax-position #'expr) '#,(syntax-span #'expr)))))] [else ;; Otherwise, leave the expression alone. #'expr])])) (define (f x) (* (skeeterize x) x)) (define (g voldemort) (* (skeeterize voldemort) voldemort)) ;; Examples: (f 7) (g 7) ;; The error should highlight the use ;; of the one-who-shall-not-be-named ;; in g.
procedure
(exn:srclocs? v) → boolean?
v : any/c
procedure
→ (exn:srclocs? . -> . (listof srcloc)) v : exn:srclocs?
struct
(struct srcloc (source line column position span) #:extra-constructor-name make-srcloc #:transparent) source : any/c line : (or/c exact-positive-integer? #f) column : (or/c exact-nonnegative-integer? #f) position : (or/c exact-positive-integer? #f) span : (or/c exact-nonnegative-integer? #f)
source —
An arbitrary value identifying the source, often a path (see Paths). line —
The line number (counts from 1) or #f (unknown). column —
The column number (counts from 0) or #f (unknown). position —
The starting position (counts from 1) or #f (unknown). span —
The number of covered positions (counts from 0) or #f (unknown).