0
# Exceptions
1
2
Exception classes for command line processing errors and program flow control. Clikt provides a comprehensive set of exceptions that are automatically caught and formatted when using `CliktCommand.main()`.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Core exception hierarchy for CLI error handling.
9
10
```kotlin { .api }
11
/**
12
* Base exception for command line processing errors
13
* @param message Error message
14
* @param cause Underlying cause
15
* @param statusCode Exit code (default 1)
16
* @param printError Whether to print to stderr (default true)
17
*/
18
open class CliktError(
19
message: String? = null,
20
cause: Exception? = null,
21
val statusCode: Int = 1,
22
val printError: Boolean = true
23
) : RuntimeException(message, cause)
24
25
/**
26
* Interface for CliktErrors that have a context attached
27
*/
28
interface ContextCliktError {
29
/** The context of the command that raised this error */
30
var context: Context?
31
}
32
33
/**
34
* Base class for user errors
35
* @param message Error message
36
* @param paramName Parameter name that caused the error
37
* @param statusCode Exit code (default 1)
38
*/
39
open class UsageError(
40
message: String?,
41
var paramName: String? = null,
42
statusCode: Int = 1
43
) : CliktError(message, statusCode = statusCode), ContextCliktError {
44
45
constructor(message: String, argument: Argument, statusCode: Int = 1)
46
constructor(message: String, option: Option, statusCode: Int = 1)
47
constructor(argument: Argument, statusCode: Int = 1)
48
constructor(option: Option, statusCode: Int = 1)
49
50
/** Format the error message for display */
51
open fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
52
53
override var context: Context?
54
}
55
```
56
57
### Program Control Exceptions
58
59
Exceptions used to control program flow and output.
60
61
```kotlin { .api }
62
/**
63
* Exception that indicates the command's help should be printed
64
* @param context Command context
65
* @param error Whether to print to stderr (default false)
66
* @param statusCode Exit code (default 0)
67
*/
68
class PrintHelpMessage(
69
override var context: Context?,
70
val error: Boolean = false,
71
statusCode: Int = 0
72
) : CliktError(printError = false, statusCode = statusCode), ContextCliktError
73
74
/**
75
* Exception that indicates a message should be printed
76
* @param message Message to print
77
* @param statusCode Exit code (default 0)
78
* @param printError Whether to print to stderr (default false)
79
*/
80
open class PrintMessage(
81
message: String,
82
statusCode: Int = 0,
83
printError: Boolean = false
84
) : CliktError(message, statusCode = statusCode, printError = printError)
85
86
/**
87
* Indicate that the program finished in a controlled manner
88
* @param statusCode Exit code
89
*/
90
open class ProgramResult(statusCode: Int) : CliktError(statusCode = statusCode)
91
92
/**
93
* Internal error that signals Clikt to abort
94
*/
95
class Abort : ProgramResult(statusCode = 1)
96
97
/**
98
* Exception that indicates shell completion code should be printed
99
*/
100
class PrintCompletionMessage(message: String) : PrintMessage(message, statusCode = 0)
101
```
102
103
### Parameter Error Exceptions
104
105
Specific exceptions for parameter validation and parsing errors.
106
107
```kotlin { .api }
108
/**
109
* Multiple usage errors occurred
110
* @param errors List of usage errors
111
*/
112
class MultiUsageError(
113
val errors: List<UsageError>
114
) : UsageError(null, statusCode = errors.first().statusCode) {
115
116
companion object {
117
/** Build MultiUsageError from error list, or return single error/null */
118
fun buildOrNull(errors: List<UsageError>): UsageError?
119
}
120
121
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
122
}
123
124
/**
125
* A parameter was given invalid format or type
126
*/
127
class BadParameterValue : UsageError {
128
constructor(message: String)
129
constructor(message: String, argument: Argument)
130
constructor(message: String, option: Option)
131
constructor(message: String, option: Option, name: String)
132
133
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
134
}
135
136
/**
137
* A required option was not provided
138
* @param option The missing option
139
*/
140
class MissingOption(option: Option) : UsageError(option) {
141
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
142
}
143
144
/**
145
* A required argument was not provided
146
* @param argument The missing argument
147
*/
148
class MissingArgument(argument: Argument) : UsageError(argument) {
149
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
150
}
151
152
/**
153
* A subcommand was provided that does not exist
154
* @param paramName The invalid subcommand name
155
* @param possibilities List of valid subcommand names
156
*/
157
class NoSuchSubcommand(
158
paramName: String,
159
private val possibilities: List<String> = emptyList()
160
) : UsageError(null, paramName) {
161
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
162
}
163
164
/**
165
* An option was provided that does not exist
166
* @param paramName The invalid option name
167
* @param possibilities List of valid option names
168
*/
169
class NoSuchOption(
170
paramName: String,
171
private val possibilities: List<String> = emptyList()
172
) : UsageError(null, paramName) {
173
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
174
}
175
176
/**
177
* An option was supplied with incorrect number of values
178
* @param minValues Expected minimum number of values
179
* @param paramName The option name
180
*/
181
class IncorrectOptionValueCount(
182
private val minValues: Int,
183
paramName: String
184
) : UsageError(null, paramName) {
185
186
constructor(option: Option, paramName: String)
187
188
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
189
}
190
191
/**
192
* An argument was supplied with incorrect number of values
193
* @param nvalues Expected number of values
194
* @param argument The argument
195
*/
196
class IncorrectArgumentValueCount(
197
val nvalues: Int,
198
argument: Argument
199
) : UsageError(argument) {
200
201
constructor(argument: Argument)
202
203
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
204
}
205
206
/**
207
* Multiple mutually exclusive options were supplied
208
* @param names List of conflicting option names
209
*/
210
class MutuallyExclusiveGroupException(
211
val names: List<String>
212
) : UsageError(null) {
213
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
214
}
215
```
216
217
### File and Configuration Exceptions
218
219
Exceptions related to file processing and configuration.
220
221
```kotlin { .api }
222
/**
223
* A required configuration file was not found
224
* @param filename The missing file name
225
*/
226
class FileNotFound(
227
val filename: String
228
) : UsageError(null) {
229
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
230
}
231
232
/**
233
* A configuration file failed to parse correctly
234
* @param filename The file that failed to parse
235
* @param message Error description
236
* @param lineno Optional line number where error occurred
237
*/
238
class InvalidFileFormat(
239
private val filename: String,
240
message: String,
241
private val lineno: Int? = null
242
) : UsageError(message) {
243
override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
244
}
245
```
246
247
## Usage Examples
248
249
### Basic Error Handling
250
251
```kotlin
252
import com.github.ajalt.clikt.core.*
253
import com.github.ajalt.clikt.parameters.options.*
254
import com.github.ajalt.clikt.parameters.arguments.*
255
256
class MyCommand : CliktCommand() {
257
private val file by argument(help = "Input file")
258
private val count by option("--count", "-c", help = "Number of items").int()
259
260
override fun run() {
261
// Throw custom error
262
if (!File(file).exists()) {
263
throw UsageError("File not found: $file")
264
}
265
266
// Validation with context
267
count?.let { c ->
268
if (c <= 0) {
269
throw BadParameterValue("Count must be positive", ::count.option)
270
}
271
}
272
}
273
}
274
275
fun main(args: Array<String>) {
276
try {
277
MyCommand().main(args)
278
} catch (e: CliktError) {
279
// Custom error handling
280
println("Error: ${e.message}")
281
exitProcess(e.statusCode)
282
}
283
}
284
```
285
286
### Program Flow Control
287
288
```kotlin
289
class VersionCommand : CliktCommand() {
290
private val version by option("--version", help = "Show version").flag()
291
292
override fun run() {
293
if (version) {
294
// Print version and exit successfully
295
throw PrintMessage("MyApp version 1.0.0")
296
}
297
}
298
}
299
300
class AbortCommand : CliktCommand() {
301
private val dangerous by option("--dangerous", help = "Dangerous operation").flag()
302
303
override fun run() {
304
if (dangerous && !confirmDangerous()) {
305
// Abort without error message
306
throw Abort()
307
}
308
}
309
310
private fun confirmDangerous(): Boolean {
311
echo("This is a dangerous operation. Are you sure? (y/N)")
312
return readLine()?.lowercase() == "y"
313
}
314
}
315
```
316
317
### Custom Validation Errors
318
319
```kotlin
320
class DatabaseCommand : CliktCommand() {
321
private val host by option("--host", help = "Database host").required()
322
private val port by option("--port", help = "Database port").int().default(5432)
323
324
override fun run() {
325
// Custom validation with proper error reporting
326
if (port !in 1..65535) {
327
throw BadParameterValue(
328
"Port must be between 1 and 65535, got $port",
329
::port.option
330
)
331
}
332
333
try {
334
connectToDatabase(host, port)
335
} catch (e: SQLException) {
336
throw UsageError("Failed to connect to database: ${e.message}")
337
}
338
}
339
}
340
```
341
342
### Multiple Error Handling
343
344
```kotlin
345
class ValidateCommand : CliktCommand() {
346
private val files by argument(help = "Files to validate").multiple()
347
348
override fun run() {
349
val errors = mutableListOf<UsageError>()
350
351
for (file in files) {
352
try {
353
validateFile(file)
354
} catch (e: Exception) {
355
errors.add(BadParameterValue("Invalid file $file: ${e.message}"))
356
}
357
}
358
359
// Throw multiple errors at once
360
MultiUsageError.buildOrNull(errors)?.let { throw it }
361
362
echo("All files validated successfully")
363
}
364
}
365
```