Tcl catch Command
last modified October 20, 2026
The Tcl catch command is used for error handling. It evaluates a
script and catches any exceptions that occur. This prevents errors from
terminating the program execution.
Basic Definition
The catch command evaluates its argument as a Tcl script. It
returns the completion code of the script: 0 (TCL_OK) if the script
executes successfully, 1 if an error occurs, and 2, 3 or 4 if the script ends
with return, break or continue.
Syntax: catch script ?resultVarName? ?optionsVarName?. The optional
arguments capture the script's result (the error message after an error) and
the return options dictionary.
Basic Error Handling
This example shows the simplest usage of catch to handle errors.
set result [catch {expr {1 / 0}} error]
puts "Result: $result, Error: $error"
This attempts to divide by zero, which would normally cause an error. The
catch command prevents termination and stores the error message.
Examining the Return Code and Options
The return code and the options dictionary tell us what kind of completion occurred.
set script {
set x "hello"
expr {$x + 10}
}
set code [catch $script result options]
if {$code == 1} {
puts "Error occurred: $result"
puts "Error code: [dict get $options -code]"
}
This catches the error raised when trying to add a number to a non-numeric
string. Here the script is kept in a variable and passed as
$script. The -code entry of the options dictionary
holds the completion code of the script (1 means error), which is the same
value that catch returned. The machine-readable error code is in
the separate -errorcode entry.
Nested Catch Commands
catch commands can be nested to handle errors at different levels.
proc riskyOperation {x} {
if {$x == 0} {
error "Division by zero attempted"
}
return [expr {100 / $x}]
}
set result [catch {
catch {riskyOperation 0} innerResult
} outerResult]
puts "Outer result: $outerResult"
puts "Inner result: $innerResult"
The inner catch traps the error of the procedure and returns 1,
and that value is the result of the script given to the outer
catch. The outer catch itself completes normally
(it returns 0, which is stored in result and not printed), so
"Outer result: 1" is the inner catch's return value, not an error. An outer
catch would only see an error if the inner one did not trap it
or the script failed in another way.
Using Finally-like Behavior
The try command has a finally clause for cleanup, but
the same effect can be achieved with catch.
set code [catch {
set file [open "nonexistent.txt" r]
set content [read $file]
} result]
if {[info exists file]} {
close $file
}
if {$code} {
puts "Error reading file: $result"
} else {
puts "File content: $content"
}
This demonstrates ensuring a file is closed whether an error occurs or not. The
open call is inside the catch block too, because it
can fail. The cleanup code executes after the catch block
regardless of outcome and closes the file only if it was opened.
Error Codes and Messages
The error command can attach an error code to the error, which
catch reports in the options dictionary.
set code [catch {error "Custom error" "" 42} result options]
puts "Error code: $code"
puts "Error message: $result"
puts "Custom error code: [dict get $options -errorcode]"
puts "Error info: [dict get $options -errorinfo]"
The third argument of error sets the -errorcode entry
(42 here), while the second one (the empty string) is the initial stack trace.
The value returned by catch is still 1, because that is the
completion code for any error. The -errorinfo entry contains the
message and a stack trace for debugging.
Advanced Error Handling
Combine catch with other commands for robust error handling.
proc safeDivide {a b} {
if {![string is integer $a] || ![string is integer $b]} {
error "Non-integer arguments" "" 1
}
if {$b == 0} {
error "Division by zero" "" 2
}
return [expr {$a / $b}]
}
set code [catch {safeDivide 10 0} result options]
switch [dict get $options -errorcode] {
1 { puts "Type error: $result" }
2 { puts "Math error: $result" }
default { puts "Unknown error: $result" }
}
Here different errors are handled differently based on the value of
-errorcode. The numbers 1 and 2 keep the example short; in real
code the error code is conventionally a list such as
{MYAPP DIVZERO}, which can be tested with lindex.
Best Practices
- Specificity: Catch only what you can handle meaningfully.
- Logging: Always log unexpected errors for debugging.
- Recovery: Provide recovery paths where possible.
- Cleanup: Ensure resources are released after errors.
- Return codes: Remember that
catchalso returns 2, 3 or 4 forreturn,breakandcontinue; test for== 1when only errors matter.
This tutorial covered the Tcl catch command with practical
examples showing its usage in different error handling scenarios.
Author
List all Tcl Tutorials.