ZetCode

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.

basic_catch.tcl
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.

catch_specific.tcl
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.

catch_nested.tcl
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.

catch_finally.tcl
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.

catch_codes.tcl
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.

catch_advanced.tcl
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

This tutorial covered the Tcl catch command with practical examples showing its usage in different error handling scenarios.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Tcl Tutorials.