CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-github-ajalt--clikt-jvm

Multiplatform command line interface parsing for Kotlin

Pending
Overview
Eval results
Files

exceptions.mddocs/

Exceptions

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().

Capabilities

Base Exception Classes

Core exception hierarchy for CLI error handling.

/**
 * Base exception for command line processing errors
 * @param message Error message
 * @param cause Underlying cause
 * @param statusCode Exit code (default 1)
 * @param printError Whether to print to stderr (default true)
 */
open class CliktError(
    message: String? = null,
    cause: Exception? = null,
    val statusCode: Int = 1,
    val printError: Boolean = true
) : RuntimeException(message, cause)

/**
 * Interface for CliktErrors that have a context attached
 */
interface ContextCliktError {
    /** The context of the command that raised this error */
    var context: Context?
}

/**
 * Base class for user errors
 * @param message Error message
 * @param paramName Parameter name that caused the error
 * @param statusCode Exit code (default 1)
 */
open class UsageError(
    message: String?,
    var paramName: String? = null,
    statusCode: Int = 1
) : CliktError(message, statusCode = statusCode), ContextCliktError {
    
    constructor(message: String, argument: Argument, statusCode: Int = 1)
    constructor(message: String, option: Option, statusCode: Int = 1)
    constructor(argument: Argument, statusCode: Int = 1)
    constructor(option: Option, statusCode: Int = 1)
    
    /** Format the error message for display */
    open fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
    
    override var context: Context?
}

Program Control Exceptions

Exceptions used to control program flow and output.

/**
 * Exception that indicates the command's help should be printed
 * @param context Command context
 * @param error Whether to print to stderr (default false)
 * @param statusCode Exit code (default 0)
 */
class PrintHelpMessage(
    override var context: Context?,
    val error: Boolean = false,
    statusCode: Int = 0
) : CliktError(printError = false, statusCode = statusCode), ContextCliktError

/**
 * Exception that indicates a message should be printed
 * @param message Message to print
 * @param statusCode Exit code (default 0)
 * @param printError Whether to print to stderr (default false)
 */
open class PrintMessage(
    message: String,
    statusCode: Int = 0,
    printError: Boolean = false
) : CliktError(message, statusCode = statusCode, printError = printError)

/**
 * Indicate that the program finished in a controlled manner
 * @param statusCode Exit code
 */
open class ProgramResult(statusCode: Int) : CliktError(statusCode = statusCode)

/**
 * Internal error that signals Clikt to abort
 */
class Abort : ProgramResult(statusCode = 1)

/**
 * Exception that indicates shell completion code should be printed
 */
class PrintCompletionMessage(message: String) : PrintMessage(message, statusCode = 0)

Parameter Error Exceptions

Specific exceptions for parameter validation and parsing errors.

/**
 * Multiple usage errors occurred
 * @param errors List of usage errors
 */
class MultiUsageError(
    val errors: List<UsageError>
) : UsageError(null, statusCode = errors.first().statusCode) {
    
    companion object {
        /** Build MultiUsageError from error list, or return single error/null */
        fun buildOrNull(errors: List<UsageError>): UsageError?
    }
    
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * A parameter was given invalid format or type
 */
class BadParameterValue : UsageError {
    constructor(message: String)
    constructor(message: String, argument: Argument)
    constructor(message: String, option: Option)
    constructor(message: String, option: Option, name: String)
    
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * A required option was not provided
 * @param option The missing option
 */
class MissingOption(option: Option) : UsageError(option) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * A required argument was not provided
 * @param argument The missing argument
 */
class MissingArgument(argument: Argument) : UsageError(argument) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * A subcommand was provided that does not exist
 * @param paramName The invalid subcommand name
 * @param possibilities List of valid subcommand names
 */
class NoSuchSubcommand(
    paramName: String,
    private val possibilities: List<String> = emptyList()
) : UsageError(null, paramName) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * An option was provided that does not exist
 * @param paramName The invalid option name
 * @param possibilities List of valid option names
 */
class NoSuchOption(
    paramName: String,
    private val possibilities: List<String> = emptyList()
) : UsageError(null, paramName) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * An option was supplied with incorrect number of values
 * @param minValues Expected minimum number of values
 * @param paramName The option name
 */
class IncorrectOptionValueCount(
    private val minValues: Int,
    paramName: String
) : UsageError(null, paramName) {
    
    constructor(option: Option, paramName: String)
    
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * An argument was supplied with incorrect number of values
 * @param nvalues Expected number of values
 * @param argument The argument
 */
class IncorrectArgumentValueCount(
    val nvalues: Int,
    argument: Argument
) : UsageError(argument) {
    
    constructor(argument: Argument)
    
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * Multiple mutually exclusive options were supplied
 * @param names List of conflicting option names
 */
class MutuallyExclusiveGroupException(
    val names: List<String>
) : UsageError(null) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

File and Configuration Exceptions

Exceptions related to file processing and configuration.

/**
 * A required configuration file was not found
 * @param filename The missing file name
 */
class FileNotFound(
    val filename: String
) : UsageError(null) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

/**
 * A configuration file failed to parse correctly
 * @param filename The file that failed to parse
 * @param message Error description
 * @param lineno Optional line number where error occurred
 */
class InvalidFileFormat(
    private val filename: String,
    message: String,
    private val lineno: Int? = null
) : UsageError(message) {
    override fun formatMessage(localization: Localization, formatter: ParameterFormatter): String
}

Usage Examples

Basic Error Handling

import com.github.ajalt.clikt.core.*
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.arguments.*

class MyCommand : CliktCommand() {
    private val file by argument(help = "Input file")
    private val count by option("--count", "-c", help = "Number of items").int()
    
    override fun run() {
        // Throw custom error
        if (!File(file).exists()) {
            throw UsageError("File not found: $file")
        }
        
        // Validation with context
        count?.let { c ->
            if (c <= 0) {
                throw BadParameterValue("Count must be positive", ::count.option)
            }
        }
    }
}

fun main(args: Array<String>) {
    try {
        MyCommand().main(args)
    } catch (e: CliktError) {
        // Custom error handling
        println("Error: ${e.message}")
        exitProcess(e.statusCode)
    }
}

Program Flow Control

class VersionCommand : CliktCommand() {
    private val version by option("--version", help = "Show version").flag()
    
    override fun run() {
        if (version) {
            // Print version and exit successfully
            throw PrintMessage("MyApp version 1.0.0")
        }
    }
}

class AbortCommand : CliktCommand() {
    private val dangerous by option("--dangerous", help = "Dangerous operation").flag()
    
    override fun run() {
        if (dangerous && !confirmDangerous()) {
            // Abort without error message
            throw Abort()
        }
    }
    
    private fun confirmDangerous(): Boolean {
        echo("This is a dangerous operation. Are you sure? (y/N)")
        return readLine()?.lowercase() == "y"
    }
}

Custom Validation Errors

class DatabaseCommand : CliktCommand() {
    private val host by option("--host", help = "Database host").required()
    private val port by option("--port", help = "Database port").int().default(5432)
    
    override fun run() {
        // Custom validation with proper error reporting
        if (port !in 1..65535) {
            throw BadParameterValue(
                "Port must be between 1 and 65535, got $port",
                ::port.option
            )
        }
        
        try {
            connectToDatabase(host, port)
        } catch (e: SQLException) {
            throw UsageError("Failed to connect to database: ${e.message}")
        }
    }
}

Multiple Error Handling

class ValidateCommand : CliktCommand() {
    private val files by argument(help = "Files to validate").multiple()
    
    override fun run() {
        val errors = mutableListOf<UsageError>()
        
        for (file in files) {
            try {
                validateFile(file)
            } catch (e: Exception) {
                errors.add(BadParameterValue("Invalid file $file: ${e.message}"))
            }
        }
        
        // Throw multiple errors at once
        MultiUsageError.buildOrNull(errors)?.let { throw it }
        
        echo("All files validated successfully")
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-github-ajalt--clikt-jvm

docs

arguments.md

configuration-sources.md

core-commands.md

exceptions.md

index.md

options.md

parameter-groups.md

parameter-types.md

shell-completion.md

testing-utilities.md

tile.json