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.Exceptions.CTException:
abstract type CTBase.Exceptions.CTException <: Exception endException Hierarchy
CTBase.Exceptions.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 dependencies
└── SolverFailure # Solver/integrator failuresGeneral 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.Exceptions.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.Exceptions.IncorrectArgument <: CTBase.Exceptions.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 CTBase
julia> algorithms = CTBase.Descriptions.add((), (:a, :b))
(:a, :b)
julia> CTBase.Descriptions.add(algorithms, (:a, :b)) # Error: duplicate
IncorrectArgument → top-level scope, REPL[3]:2
│
│ the description (:a, :b) is already in ((:a, :b),)
│
│ Got (:a, :b)
│ Expected a unique description not in the catalog
│
│ Context description catalog management
│ Hint Check existing descriptions before adding, or use a different description
└─Using invalid indices for the Unicode helpers:
julia> using CTBase
julia> CTBase.Unicode.ctindice(-1) # Error: must be between 0 and 9
IncorrectArgument → top-level scope, REPL[2]:2
│
│ the subscript must be positive
│
│ Got -1
│ Expected ≥ 0
│
│ Context Unicode subscript generation
└─Use this exception whenever one input value is outside the allowed domain (wrong range, duplicate, empty when it must not be, etc.).
AmbiguousDescription
CTBase.Exceptions.AmbiguousDescription <: CTBase.Exceptions.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 CTBase
julia> D = ((:a, :b), (:a, :b, :c), (:b, :c))
(:a, :b)
(:a, :b, :c)
(:b, :c)
julia> CTBase.Descriptions.complete(:f; descriptions=D) # Error: no match found
AmbiguousDescription → top-level scope, REPL[3]:2
│
│ cannot find matching description
│
│ Diagnostic Unknown symbols — none of the requested symbols appear in any available description
│ Requested (:f,)
│ Available (:a, :b)
│ (:a, :b, :c)
│ (:b, :c)
│
│ Context description completion
│ Hint 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.Exceptions.PreconditionError <: CTBase.Exceptions.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.Exceptions.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.Exceptions.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:
CTBase.Exceptions.IncorrectArgument: The value of an argument is wrongCTBase.Exceptions.PreconditionError: The timing or state is wrong
Implementation Exceptions
NotImplemented
CTBase.Exceptions.NotImplemented <: CTBase.Exceptions.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.Exceptions.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.Exceptions.ParsingError <: CTBase.Exceptions.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)
Example:
julia> using CTBase
julia> throw(CTBase.Exceptions.ParsingError(
"unexpected token 'end'",
location="line 42, column 10",
suggestion="Check for unmatched 'begin' or remove extra 'end'"
))
ParsingError → top-level scope, REPL[2]:2
│
│ unexpected token 'end'
│
│ Location line 42, column 10
│
│ Hint Check for unmatched 'begin' or remove extra 'end'
└─Use this exception when parsing user input, configuration files, or DSL expressions.
ExtensionError
CTBase.Exceptions.ExtensionError <: CTBase.Exceptions.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.Exceptions.ExtensionError(
:Plots,
feature="result visualization",
context="plot_results function"
))
endThe enriched display automatically suggests:
julia> using CTBase
julia> throw(CTBase.Exceptions.ExtensionError(:Plots, feature="result visualization", context="plot_results function"))
ExtensionError → top-level scope, REPL[2]:2
│
│ missing dependencies
│
│ Missing Plots
│
│ Context plot_results function
│ Hint Run: using Plots
└─Use this exception when:
A feature requires optional packages
Extensions are not loaded
Weak dependencies are missing
Solver and Integrator Exceptions
SolverFailure
CTBase.Exceptions.SolverFailure <: CTBase.Exceptions.CTExceptionWhen to use: Thrown when a solver (ODE integrator, optimization solver, linear solver, etc.) fails to complete successfully or returns an error code.
Fields:
msg::String: Error message describing the failureretcode::Union{String,Nothing}: Solver-specific return code (optional)suggestion::Union{String,Nothing}: How to fix the problem (optional)context::Union{String,Nothing}: Where the error occurred (optional)
Example:
using CTBase
function integrate_ode(system, integrator)
result = solve(system, integrator)
if result.retcode != :Success
throw(CTBase.Exceptions.SolverFailure(
"ODE integration failed",
retcode=string(result.retcode),
suggestion="Reduce time step or check initial conditions",
context="SciML integrator"
))
end
return result
endThe enriched display shows the solver-specific return code:
julia> throw(CTBase.Exceptions.SolverFailure("ODE integration failed", retcode=":Unstable", suggestion="Reduce time step or check initial conditions", context="SciML integrator"))
SolverFailure → top-level scope, REPL[1]:2
│
│ ODE integration failed
│
│ Retcode :Unstable
│
│ Context SciML integrator
│ Hint Reduce time step or check initial conditions
└─Common return codes:
SciML integrators:
:Unstable,:DtLessThanMin,:MaxIters,:SuccessNLP solvers:
:Infeasible,:MaxIterations,:Stalled,:FirstOrderLinear solvers: Condition number indicators, singular matrix flags
Use this exception when:
ODE integration fails in CTFlows
Optimization solver does not converge in CTDirect
Linear system is ill-conditioned
Any numerical solver returns a failure status
Distinction from other exceptions:
CTBase.Exceptions.IncorrectArgument: The input is invalidCTBase.Exceptions.PreconditionError: The state or timing is wrongCTBase.Exceptions.SolverFailure: The numerical computation itself failed
Quick Reference: Which Exception to Use?
| Situation | Exception | Example |
|---|---|---|
| Invalid argument value | CTBase.Exceptions.IncorrectArgument | throw(IncorrectArgument("x must be > 0", got="-5", expected="> 0")) |
| Wrong function call order | CTBase.Exceptions.PreconditionError | throw(PreconditionError("Must initialize before configure")) |
| Unimplemented interface | CTBase.Exceptions.NotImplemented | throw(NotImplemented("run! not implemented for MyType")) |
| Parsing error | CTBase.Exceptions.ParsingError | throw(ParsingError("unexpected token", location="line 10")) |
| Ambiguous description | CTBase.Exceptions.AmbiguousDescription | throw(AmbiguousDescription((:x,), candidates=["(:a,:b)", "(:c,:d)"])) |
| Missing optional dependency | CTBase.Exceptions.ExtensionError | throw(ExtensionError(:Plots, feature="plotting")) |
| Solver/integrator failure | CTBase.Exceptions.SolverFailure | throw(SolverFailure("ODE failed", retcode=":Unstable")) |
Enriched Error Display
All CTBase exceptions provide an enriched, user-friendly display with:
Type name on line 1, with caller location when available
Pipe-box layout using
│separators for visual structureAligned labels (Got, Expected, Reason, Hint, Context, …) padded to a common width
Color coding: red for type name, yellow for warnings, green for hints
Dynamic Hint for
ExtensionErrorgenerated fromweakdeps
Example of enriched display:
PreconditionError → configure!, MyModule.jl:42
│
│ System must be initialized before configuration
│
│ Reason initialize! not called yet
│
│ Context system configuration
│ Hint Call initialize!(state) before configure!
└─This 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 exceptionsDon't hide errors: Only catch exceptions you can handle
See Also
Descriptions Tutorial: Understanding the description system
Test Runner Guide: Testing exception handling