Error Handling and CTBase Exceptions
CTBase defines a small hierarchy of domain-specific exceptions to make error handling explicit and consistent across the control-toolbox ecosystem.
All custom exceptions inherit from CTBase.CTException:
abstract type CTBase.CTException <: Exception endException Hierarchy
CTException (abstract)
├── IncorrectArgument # Input validation errors
├── PreconditionError # Order of operations, state validation
├── NotImplemented # Unimplemented interface methods
├── ParsingError # Parsing errors
├── AmbiguousDescription # Ambiguous or incorrect descriptions
└── ExtensionError # Missing optional dependenciesGeneral Error Handling Pattern
You should generally catch exceptions like this:
try
# call into CTBase or a package built on top of it
catch e
if e isa CTBase.CTException
# handle CTBase domain errors in a uniform way
@warn "CTBase error" exception=(e, catch_backtrace())
else
# non-CTBase error: rethrow so it is not hidden
rethrow()
end
endThis pattern avoids accidentally swallowing unrelated internal errors while still giving you a single place to handle all CTBase-specific problems.
Input Validation Exceptions
IncorrectArgument
CTBase.IncorrectArgument <: CTBase.CTExceptionWhen to use: Thrown when an individual argument is invalid or violates a constraint.
Fields:
msg::String: Error messagegot::Union{String,Nothing}: The invalid value received (optional)expected::Union{String,Nothing}: What was expected (optional)suggestion::Union{String,Nothing}: How to fix the problem (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Examples:
Adding a duplicate description:
julia> using CTBasejulia> algorithms = CTBase.add((), (:a, :b))(:a, :b)julia> CTBase.add(algorithms, (:a, :b)) # Error: duplicateERROR: Control Toolbox Error ❌ Error: CTBase.Exceptions.IncorrectArgument, the description (:a, :b) is already in ((:a, :b),) 🔍 Got: (:a, :b), Expected: a unique description not in the catalog 📂 Context: description catalog management 💡 Suggestion: Check existing descriptions before adding, or use a different description
Using invalid indices for the Unicode helpers:
julia> using CTBasejulia> CTBase.ctindice(-1) # Error: must be between 0 and 9ERROR: Control Toolbox Error ❌ Error: CTBase.Exceptions.IncorrectArgument, the subscript must be between 0 and 9 🔍 Got: -1, Expected: 0-9 📂 Context: Unicode subscript generation 💡 Suggestion: Use ctindices() for numbers larger than 9, or check your input value
Use this exception whenever one input value is outside the allowed domain (wrong range, duplicate, empty when it must not be, etc.).
AmbiguousDescription
CTBase.AmbiguousDescription <: CTBase.CTExceptionWhen to use: Thrown when a description (a tuple of Symbols) cannot be matched to any known valid description.
Fields:
description::Description: The ambiguous descriptioncandidates::Union{Vector{String},Nothing}: Suggested alternatives (optional)suggestion::Union{String,Nothing}: How to fix the problem (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Example:
julia> using CTBasejulia> D = ((:a, :b), (:a, :b, :c), (:b, :c))(:a, :b) (:a, :b, :c) (:b, :c)julia> CTBase.complete(:f; descriptions=D) # Error: no match foundERROR: Control Toolbox Error ❌ Error: CTBase.Exceptions.AmbiguousDescription, cannot find matching description ⚠️ Diagnostic: Unknown symbols - none of the requested symbols appear in any available description 🎯 Requested description: (:f,) 📋 Available descriptions: - (:a, :b) - (:a, :b, :c) - (:b, :c) 📂 Context: description completion 💡 Suggestion: Choose from the available descriptions listed above
Use this exception when the high-level choice of description itself is wrong or ambiguous and there is no sensible default.
Precondition and State Exceptions
PreconditionError
CTBase.PreconditionError <: CTBase.CTExceptionWhen to use: Thrown when a function is called in the wrong order or when the system is in an invalid state.
Fields:
msg::String: Error messagereason::Union{String,Nothing}: Why the precondition failed (optional)suggestion::Union{String,Nothing}: How to fix the problem (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Examples:
System initialization order:
function configure!(state::SystemState, config::Dict)
if !state.initialized
throw(CTBase.PreconditionError(
"System must be initialized before configuration",
reason="initialize! not called yet",
suggestion="Call initialize!(state) before configure!",
context="system configuration"
))
end
# ... configure system ...
endState validation:
function dynamics!(ocp::PreModel, f::Function)
if !__is_state_set(ocp)
throw(CTBase.PreconditionError(
"State must be set before defining dynamics",
reason="state has not been defined yet",
suggestion="Call state!(ocp, dimension) before dynamics!",
context="dynamics! function"
))
end
# ... set dynamics ...
endUse this exception for:
- Functions called in the wrong order
- Operations on uninitialized objects
- State machine violations
- Workflow step dependencies
Distinction from IncorrectArgument:
IncorrectArgument: The value of an argument is wrongPreconditionError: The timing or state is wrong
Implementation Exceptions
NotImplemented
CTBase.NotImplemented <: CTBase.CTExceptionWhen to use: Used to mark interface points that must be implemented by concrete subtypes.
Fields:
msg::String: Error messagerequired_method::Union{String,Nothing}: Method signature that needs implementation (optional)suggestion::Union{String,Nothing}: How to implement (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Example:
The typical pattern is to provide a method on an abstract type that throws NotImplemented, and then override it in each concrete implementation:
abstract type MyAbstractAlgorithm end
function run!(algo::MyAbstractAlgorithm, state)
throw(CTBase.NotImplemented(
"run! is not implemented for $(typeof(algo))",
required_method="run!(::$(typeof(algo)), state)",
suggestion="Implement run! for your algorithm type",
context="algorithm execution"
))
end
# Concrete implementation
struct MyConcreteAlgorithm <: MyAbstractAlgorithm end
function run!(algo::MyConcreteAlgorithm, state)
# actual implementation
endUse this exception when defining interfaces and you want an explicit, typed error rather than a generic error("TODO").
Parsing and Extension Exceptions
ParsingError
CTBase.ParsingError <: CTBase.CTExceptionWhen to use: Intended for errors detected during parsing of input structures or DSLs (domain-specific languages).
Fields:
msg::String: Error messagelocation::Union{String,Nothing}: Where in the input the error occurred (optional)suggestion::Union{String,Nothing}: How to fix the syntax (optional)context::Union{String,Nothing}: What was being parsed (optional)
Example:
julia> using CTBasejulia> throw(CTBase.ParsingError( "unexpected token 'end'", location="line 42, column 10", suggestion="Check for unmatched 'begin' or remove extra 'end'", context="control flow parsing" ))ERROR: MethodError: no method matching CTBase.Exceptions.ParsingError(::String; location::String, suggestion::String, context::String) This method does not support all of the given keyword arguments (and may not support any). Closest candidates are: CTBase.Exceptions.ParsingError(::String; location, suggestion) got unsupported keyword argument "context" @ CTBase ~/work/CTBase.jl/CTBase.jl/src/Exceptions/types.jl:332
Use this exception when parsing user input, configuration files, or DSL expressions.
ExtensionError
CTBase.ExtensionError <: CTBase.CTExceptionWhen to use: Thrown when a feature requires optional dependencies (weak dependencies) that are not loaded.
Fields:
msg::String: Error messageweakdeps::Tuple{Vararg{Symbol}}: Names of missing packagesfeature::Union{String,Nothing}: What feature needs the dependencies (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Example:
function plot_results(data)
throw(CTBase.ExtensionError(
:Plots,
feature="result visualization",
context="plot_results function"
))
endThe enriched display automatically suggests:
❌ Error: ExtensionError, missing dependencies
📦 Missing dependencies: Plots
💡 Suggestion: julia> using PlotsUse this exception when:
- A feature requires optional packages
- Extensions are not loaded
- Weak dependencies are missing
Quick Reference: Which Exception to Use?
| Situation | Exception | Example |
|---|---|---|
| Invalid argument value | IncorrectArgument | throw(IncorrectArgument("x must be > 0", got="-5", expected="> 0")) |
| Wrong function call order | PreconditionError | throw(PreconditionError("Must initialize before configure")) |
| Unimplemented interface | NotImplemented | throw(NotImplemented("run! not implemented for MyType")) |
| Parsing error | ParsingError | throw(ParsingError("unexpected token", location="line 10")) |
| Ambiguous description | AmbiguousDescription | throw(AmbiguousDescription((:x,), candidates=["(:a,:b)", "(:c,:d)"])) |
| Missing optional dependency | ExtensionError | throw(ExtensionError(:Plots, feature="plotting")) |
Enriched Error Display
All CTBase exceptions provide an enriched, user-friendly display with:
- 🎯 Clear error type and message
- 📋 Contextual information (got/expected, reason, location)
- 💡 Actionable suggestions for fixing the problem
- 📍 User code location tracking
- 🎨 Emoji-based visual hierarchy
Example of enriched display:
Control Toolbox Error
❌ Error: PreconditionError, System must be initialized before configuration
❓ Reason: initialize! not called yet
📂 Context: system configuration
💡 Suggestion: Call initialize!(state) before configure!
📍 In your code:
configure! at MyModule.jl:42This makes debugging faster by providing all the information needed to understand and fix the problem.
Best Practices
- Choose the right exception type: Use the decision table above
- Provide context: Always fill in optional fields when available
- Be specific: Include actual values in error messages
- Suggest solutions: Help users fix the problem
- Catch specifically: Use
e isa SpecificExceptionrather than catching all exceptions - Don't hide errors: Only catch exceptions you can handle
See Also
- Descriptions Tutorial: Understanding the description system
- Test Runner Guide: Testing exception handling