Skip to content

Error Messages Reference ​

This page catalogues all exception types used in CTSolvers, with live examples and recommended fixes. CTSolvers uses enriched exceptions from CTBase.Exceptions that carry structured fields (got, expected, suggestion, context) for actionable error messages.

Exception Types ​

CTSolvers uses five exception types from CTBase.Exceptions:

TypePurpose
NotImplementedContract method not implemented by a concrete type
IncorrectArgumentInvalid argument value, type, or routing
ExtensionErrorRequired package extension not loaded
PreconditionErrorA required precondition is not met (e.g. no ODE algorithm available)
SolverFailureA backend reported a failure (e.g. unsuccessful ODE retcode)

A type parameter outside a strategy's bound (e.g. Solvers.Ipopt{GPU} for a CPU-only solver) is rejected by Julia itself with a TypeError, before any CTSolvers code runs.

All CTSolvers exceptions accept keyword arguments for structured messages:

julia
using CTSolvers
using CTBase: CTBase
const Exceptions = CTBase.Exceptions

NotImplemented — Contract Not Implemented ​

Thrown when a concrete type doesn't implement a required contract method.

Strategy contract — missing id ​

julia
abstract type IncompleteStrategy <: CTBase.Strategies.AbstractStrategy end
julia> CTBase.Strategies.id(IncompleteStrategy)
ERROR: NotImplemented
│
│  Strategy ID method not implemented
│
│  Method   id(::Type{<:Main.IncompleteStrategy})
│
│  Context  AbstractStrategy.id - required method implementation
│  Hint     Implement id(::Type{<:Main.IncompleteStrategy}) to return a unique Symbol identifier
└─

Fix: Implement the missing method:

julia
CTBase.Strategies.id(::Type{<:IncompleteStrategy}) = :my_strategy

Strategy contract — missing metadata ​

julia> CTBase.Strategies.metadata(IncompleteStrategy)
ERROR: NotImplemented
│
│  Strategy metadata method not implemented
│
│  Method   metadata(::Type{<:Main.IncompleteStrategy})
│
│  Context  AbstractStrategy.metadata - required method implementation
│  Hint     Implement metadata(::Type{<:Main.IncompleteStrategy}) to return StrategyMetadata with OptionDefinitions
└─

Optimization problem contract — missing build_model ​

Define a problem type and a modeler, but no (problem, modeler) method. A dedicated block label (optprob) keeps these throwaway types out of the shared errors scope:

julia
using CTSolvers
using CTSolvers: Optimization, Modelers
struct MinimalProblem <: Optimization.AbstractOptimizationProblem end
struct MinimalModeler <: Modelers.AbstractNLPModeler end

The generic stub in Modelers/contract.jl throws NotImplemented as soon as build_model is called with a (problem, modeler) pair for which no concrete method exists:

julia> Optimization.build_model(MinimalProblem(), nothing, MinimalModeler())
ERROR: NotImplemented
│
│  Model building not implemented
│
│  Method   Optimization.build_model(prob::Main.MinimalProblem, initial_guess, modeler::Main.MinimalModeler)
│
│  Context  Modelers.build_model - required method implementation
│  Hint     Implement build_model for this (problem, modeler) pair in the package providing the problem
└─

Fix: Implement build_model and build_solution in the package providing the problem, dispatching on the concrete (problem, modeler) pair (see Implementing an Optimization Problem).

Where it's thrown ​

MethodContext
CTBase.Strategies.id(::Type{T})Strategy type missing id
CTBase.Strategies.metadata(::Type{T})Strategy type missing metadata
CTBase.Strategies.options(strategy)Strategy instance has no options field and no custom getter
Optimization.build_model(prob, init, modeler)No concrete method for this (problem, modeler) pair
Optimization.build_solution(built, stats, modeler)No concrete method for this (built, modeler) pair

IncorrectArgument — Invalid Arguments ​

Thrown for invalid values, types, or routing errors. This is the most common exception in CTSolvers.

Type mismatch in extraction ​

When extract_option receives a value of the wrong type:

julia> def = CTBase.Options.OptionDefinition(
           name = :max_iter, type = Integer, default = 100,
           description = "Maximum iterations",
       )
max_iter::Integer (default: 100)

julia> CTBase.Options.extract_option((max_iter = "hello",), def)
ERROR: IncorrectArgument
│
│  Invalid option type
│
│  Got       value hello of type String
│  Expected  Integer
│
│  Context   Option extraction for max_iter
│  Hint      Ensure the option value matches the expected type
└─

Fix: Provide a value of the correct type.

Validator failure ​

When a value doesn't satisfy the validator constraint:

julia
bad_def = CTBase.Options.OptionDefinition(
    name = :tol, type = Real, default = 1e-8,
    description = "Tolerance",
    validator = x -> x > 0 || throw(Exceptions.IncorrectArgument(
        "Invalid tolerance value",
        got = "tol=$x",
        expected = "positive real number (> 0)",
        suggestion = "Provide a positive tolerance (e.g., 1e-6, 1e-8)",
        context = "tol validation",
    )),
)
julia> CTBase.Options.extract_option((tol = -1.0,), bad_def)
ERROR: IncorrectArgument
│
│  Invalid tolerance value
│
│  Got       tol=-1.0
│  Expected  positive real number (> 0)
│
│  Context   tol validation
│  Hint      Provide a positive tolerance (e.g., 1e-6, 1e-8)
└─

Fix: Provide a value that satisfies the validator constraint.

Type mismatch in OptionDefinition constructor ​

When the default value doesn't match the declared type:

julia> CTBase.Options.OptionDefinition(
           name = :count, type = Integer, default = "hello",
           description = "A count",
       )
ERROR: IncorrectArgument
│
│  Type mismatch in option definition
│
│  Got       default value hello of type String
│  Expected  value of type Integer
│
│  Context   OptionDefinition constructor - validating type compatibility
│  Hint      Ensure the default value matches the declared type, or adjust the type parameter
└─

Fix: Ensure the default value matches the declared type.

Invalid OptionValue source ​

julia> CTBase.Options.OptionValue(42, :invalid_source)
ERROR: IncorrectArgument
│
│  Invalid option source
│
│  Got       source=invalid_source
│  Expected  :default, :user, or :computed
│
│  Context   OptionValue constructor - validating source provenance
│  Hint      Use one of the valid source symbols: :default (tool default), :user (user-provided), or :computed (derived)
└─

Fix: Use :default, :user, or :computed.

Invalid output grid in regrid ​

Integrators.regrid checks that the new grid has at least two times, is strictly monotone in the integration direction and lies in the integration span:

julia
using CTSolvers: Integrators
using OrdinaryDiffEqTsit5
using SciMLBase: ODEProblem
using CommonSolve: CommonSolve
prob = ODEProblem((u, p, t) -> -u, 1.0, (0.0, 1.0))
r = CommonSolve.solve(prob, Integrators.SciML())
julia> Integrators.regrid(r, [0.0, 2.0])
ERROR: IncorrectArgument
│
│  The output grid must lie in the integration span
│
│  Got       [0.0, 2.0]
│  Expected  times in [0.0, 1.0]
│
│  Context   Integrators.regrid
└─

Fix: Use a grid inside (t0, tf), e.g. Integrators.regrid(r, 0:0.25:1).

ExtensionError — Extension Not Loaded ​

Thrown when a solver requires a package extension that hasn't been loaded.

julia> CTSolvers.Solvers.MadNLP()
ERROR: ExtensionError
│
│  missing dependencies to create MadNLP, access options, and solve problems
│
│  Missing  MadNLP
│
│  Context  Load MadNLP extension first: using MadNLP
│  Hint     Run: using MadNLP
└─

Fix: Load the required package before using the solver:

julia
using MadNLP  # loads the CTSolversMadNLP extension
solver = Solvers.MadNLP(max_iter = 1000)

Where it's thrown ​

StrategyRequired packages
Solvers.IpoptNLPModelsIpopt
Solvers.MadNLPMadNLP
Solvers.MadNCLMadNCL, MadNLP
Solvers.KnitroNLPModelsKnitro
Solvers.UnoUnoSolver
Solvers.MadNLP{GPU}, Solvers.MadNCL{GPU}MadNLPGPU, CUDA, CUDSS (in addition)
Modelers.ADNLPADNLPModels
Modelers.ExaExaModels (and CUDA for Modelers.Exa{GPU})
Integrators.SciMLOrdinaryDiffEqTsit5, OrdinaryDiffEq, or DifferentialEquations

Some ADNLP backends also need their AD package (Enzyme, Zygote).

PreconditionError — Missing Precondition ​

Thrown when Integrators.SciML is built with the SciML extension active (SciMLBase and DiffEqBase loaded) but no algorithm is available: no explicit alg and OrdinaryDiffEqTsit5 not loaded to provide the default Tsit5().

julia> using OrdinaryDiffEqVerner

julia> CTSolvers.Integrators.SciML()
ERROR: PreconditionError
│
│  No ODE algorithm specified and OrdinaryDiffEqTsit5 is not loaded
│
│  Reason   alg is missing
│
│  Context  SciML integrator construction
│  Hint     Load OrdinaryDiffEqTsit5: using OrdinaryDiffEqTsit5
│           Or specify an algorithm explicitly, for example:
│             SciML(alg=Vern6())
│             Flow(ocp, law; alg=Vern6())
│           Note: when specifying an algorithm, also load its package (e.g., using OrdinaryDiffEqVerner for Vern6)
└─

Fix: Pass an algorithm explicitly, Integrators.SciML(alg = Vern6()), or load OrdinaryDiffEqTsit5.

SolverFailure — Backend Failure ​

Thrown by CommonSolve.solve(prob, ::Integrators.SciML) when the ODE solver returns an unsuccessful retcode, e.g. a solution that blows up in finite time:

julia
julia> prob = ODEProblem((u, p, t) -> u^2, 1.0, (0.0, 2.0));

julia> CommonSolve.solve(prob, Integrators.SciML())
SolverFailure
│  ODE integration failed
│  Retcode  Unstable
│  Context  SciML solve
│  Hint     Try tightening tolerances (reltol, abstol) or changing the solver algorithm.

Fix: Check the model, tighten the tolerances or change the algorithm. To inspect the partial result instead, pass unsafe = true: no exception is thrown and Integrators.successful(r) is false, Integrators.status(r) is :Unstable.

Best Practices for Error Messages ​

When implementing new validators or error paths, follow the CTSolvers convention:

julia
throw(Exceptions.IncorrectArgument(
    "Short, clear description of the problem",
    got        = "what the user actually provided",
    expected   = "what was expected instead",
    suggestion = "actionable fix the user can apply",
    context    = "ModuleName.function_name - specific validation step",
))
  • got: Show the actual value, including its type if relevant

  • expected: Be specific about valid values or ranges

  • suggestion: Provide a concrete example the user can copy

  • context: Include the module and function name for traceability