Handling Errors in Tcl
By default, an uncaught error in Tcl - a missing file, a divide by zero, an undefined variable - unwinds the call stack and terminates the script with a message printed to stderr. The catch command intercepts this: catch {risky code} result stores either the successful return value or the error message in result and returns 0 on success or a non-zero code (1 for a generic error) on failure, letting a script decide how to react instead of crashing outright. Since Tcl 8.6, the try command builds on the same underlying mechanism but with clearer, block-structured syntax closer to exception handling in other languages.
Cricket analogy: catch intercepting an error before it propagates is like a wicketkeeper diving to stop a wayward throw from reaching the boundary - the play continues under control instead of conceding runs by default.
Using catch and Inspecting Errors
catch {expr {1 / 0}} err sets err to 'divide by zero' and catch returns 1; the fuller three-argument form catch {code} result optionsVar additionally populates optionsVar with a dictionary containing -errorcode, -errorinfo (the full traceback), and -errorline, letting a handler distinguish, say, a file-not-found condition from a permission-denied one by inspecting -errorcode rather than pattern-matching the human-readable message string, which can change wording between Tcl versions. The command 'error message ?info? ?code?' is how a script raises its own catchable errors, optionally supplying a custom errorcode list for callers to match on.
Cricket analogy: Matching on -errorcode rather than the message string is like an umpire's decision review checking the specific rule code violated rather than a commentator's paraphrased description of what happened.
proc safeDivide {a b} {
if {[catch {expr {$a / $b}} result options]} {
set code [dict get $options -errorcode]
if {[lindex $code 0] eq "ARITH" && [lindex $code 1] eq "DIVZERO"} {
return -code error "cannot divide $a by zero"
}
return -options $options $result
}
return $result
}
puts [safeDivide 10 2]
if {[catch {safeDivide 5 0} msg]} {
puts "handled: $msg"
}Structured Handling with try, on, and finally
try { risky code } on ok {result} { ... } trap {ARITH DIVZERO} {msg} { ... } finally { cleanup } reads far closer to exception handling in Python or Java than nested catch calls: 'on ok' fires only when the body succeeds, 'trap' matches a specific -errorcode pattern (with wildcards allowed at each list position), and 'finally' always executes regardless of whether an error occurred, making it the natural place for resource cleanup like closing a channel opened earlier in the same block. Multiple trap clauses can be stacked to handle different error categories with different recovery logic in one readable block instead of nested if/catch chains.
Cricket analogy: finally always running regardless of the outcome is like a ground staff's pitch-covering routine executing whether the day's play finished normally or was abandoned to rain - the tarps go on either way.
try's 'on' clause supports multiple result codes beyond 'ok', including 'error', 'return', 'break', and 'continue' - this lets a single try block react differently depending on exactly how the body exited, which is useful when the guarded code itself contains loop-control statements.
A bare 'catch {code}' with no result variable silently discards the error message entirely, only telling you that something failed via the return code - always capture at least the result variable (and ideally the options dictionary) so a failure can be logged or reported meaningfully rather than swallowed.
- Uncaught errors unwind the stack and terminate a Tcl script by default; catch intercepts this.
- catch {code} result returns 0 on success and a non-zero code on failure, storing the outcome in result.
- The three-argument catch {code} result options exposes -errorcode, -errorinfo, and -errorline for detailed handling.
- error message ?info? ?code? raises a custom, catchable error with an optional structured error code.
- try/on/trap/finally (Tcl 8.6+) offers block-structured error handling closer to exceptions in other languages.
- trap matches specific -errorcode patterns, allowing different recovery logic per error category.
- finally always executes, making it the right place for guaranteed cleanup like closing channels.
Practice what you learned
1. What does catch {code} result return when the code inside the braces raises an error?
2. What additional information does the three-argument form of catch (with an options variable) provide?
3. In a try block, which clause always executes regardless of whether an error occurred?
4. What does the trap clause in a try block match against?
5. What happens if you call catch {code} with no result variable at all?
Was this page helpful?
You May Also Like
File I/O in Tcl
Reading, writing, and managing files and channels in Tcl using open, read, gets, puts, and the channel configuration commands.
Namespaces in Tcl
How Tcl's namespace command organizes procedures and variables into separate scopes to avoid naming collisions in larger programs.
Tcl Object-Orientation with TclOO
How to define classes, objects, methods, and inheritance in Tcl using the built-in TclOO framework (oo::class, oo::define, oo::copy).
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics