Specialized error classes for handling command-line parsing errors, invalid arguments, custom error scenarios with exit code management, and comprehensive error handling patterns.
Base error class for all Commander-related errors with exit code and error code support.
/**
* Base error class for Commander errors
* Extends the standard Error class with exit codes and error identification
*/
class CommanderError extends Error {
/**
* Create a new CommanderError
* @param exitCode - Suggested exit code for process.exit
* @param code - Error code identifier for programmatic handling
* @param message - Human-readable error message
*/
constructor(exitCode: number, code: string, message: string);
// Properties
exitCode: number; // Suggested exit code
code: string; // Error code identifier
message: string; // Error message
nestedError?: string; // Optional nested error information
}Usage Examples:
import { CommanderError } from 'commander';
// Custom error handling
try {
program.parse(process.argv);
} catch (error) {
if (error instanceof CommanderError) {
console.error(`Error ${error.code}: ${error.message}`);
process.exit(error.exitCode);
}
throw error;
}
// Creating custom CommanderError
throw new CommanderError(2, 'custom.validation', 'Custom validation failed');Specialized error for invalid command arguments or option values.
/**
* Error for invalid command arguments or option values
* Extends CommanderError with predefined exit code and error code
*/
class InvalidArgumentError extends CommanderError {
/**
* Create a new InvalidArgumentError
* @param message - Explanation of why the argument is invalid
*/
constructor(message: string);
}Usage Examples:
import { InvalidArgumentError } from 'commander';
// In custom argument parser
const parsePort = (value) => {
const port = parseInt(value);
if (isNaN(port) || port < 1 || port > 65535) {
throw new InvalidArgumentError(`Port must be a number between 1 and 65535, got: ${value}`);
}
return port;
};
// In option argument parser
const parsePercentage = (value) => {
const num = parseFloat(value);
if (isNaN(num) || num < 0 || num > 100) {
throw new InvalidArgumentError(`Percentage must be between 0 and 100, got: ${value}`);
}
return num;
};
program
.option('-p, --port <number>', 'server port', parsePort)
.option('--opacity <percent>', 'opacity percentage', parsePercentage);Deprecated alias for InvalidArgumentError, maintained for backward compatibility.
/**
* Deprecated alias for InvalidArgumentError
* @deprecated Use InvalidArgumentError instead
*/
const InvalidOptionArgumentError = InvalidArgumentError;Built-in error handling methods on the Command class.
interface Command {
/**
* Display error message and exit
* @param message - Error message to display
* @param errorOptions - Error configuration options
*/
error(message: string, errorOptions?: ErrorOptions): never;
/**
* Override exit behavior for error handling
* @param callback - Custom exit handler
* @returns this command for chaining
*/
exitOverride(callback?: (err: CommanderError) => never | void): this;
}
interface ErrorOptions {
/** Error code identifier */
code?: string;
/** Suggested exit code */
exitCode?: number;
}Commander uses specific error codes for different parsing and validation scenarios:
// Built-in error codes used by Commander
const COMMANDER_ERROR_CODES = {
// Command parsing errors
'commander.unknownCommand': 'Unknown command specified',
'commander.unknownOption': 'Unknown option specified',
'commander.missingMandatoryOptionValue': 'Required option value not provided',
'commander.optionMissingArgument': 'Option requires an argument',
'commander.invalidArgument': 'Invalid argument value provided',
'commander.missingArgument': 'Required argument not provided',
'commander.excessArguments': 'Too many arguments provided',
// Option conflicts and requirements
'commander.conflictingOption': 'Conflicting options specified',
'commander.missingMandatoryOption': 'Required option not specified',
// Help and version
'commander.help': 'Help requested',
'commander.version': 'Version requested'
};Usage Examples:
// Display error and exit
if (!process.env.API_KEY) {
program.error('API_KEY environment variable is required', {
code: 'missing.apikey',
exitCode: 1
});
}
// Custom exit handling
program.exitOverride((err) => {
if (err.code === 'commander.invalidArgument') {
console.error(`Invalid input: ${err.message}`);
console.error('Run with --help for usage information');
}
process.exit(err.exitCode);
});// File existence validation
const validateFileExists = (filepath) => {
if (!fs.existsSync(filepath)) {
throw new InvalidArgumentError(`File not found: ${filepath}`);
}
return filepath;
};
// Format validation
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new InvalidArgumentError(`Invalid email format: ${email}`);
}
return email;
};
// Range validation
const validateRange = (min, max) => (value) => {
const num = Number(value);
if (isNaN(num) || num < min || num > max) {
throw new InvalidArgumentError(`Value must be between ${min} and ${max}, got: ${value}`);
}
return num;
};
program
.option('-f, --file <path>', 'input file', validateFileExists)
.option('-e, --email <address>', 'email address', validateEmail)
.option('-p, --port <number>', 'port number', validateRange(1, 65535));// Using built-in choices validation
program
.option('-l, --log-level <level>', 'logging level')
.choices(['error', 'warn', 'info', 'debug']);
// Custom choice validation with suggestions
const validateLogLevel = (value) => {
const validLevels = ['error', 'warn', 'info', 'debug', 'trace'];
if (!validLevels.includes(value)) {
const suggestions = validLevels.filter(level =>
level.startsWith(value) || value.startsWith(level)
);
const suggestion = suggestions.length > 0
? ` Did you mean: ${suggestions.join(', ')}?`
: '';
throw new InvalidArgumentError(`Invalid log level: ${value}.${suggestion}`);
}
return value;
};// Async validation in action handlers
program
.command('deploy <environment>')
.action(async (environment) => {
try {
const config = await loadEnvironmentConfig(environment);
await deploy(config);
} catch (error) {
if (error.code === 'ENOENT') {
program.error(`Environment configuration not found: ${environment}`, {
code: 'deploy.config.missing',
exitCode: 1
});
}
throw error;
}
});// Command suggestion on unknown commands
program.exitOverride((err) => {
if (err.code === 'commander.unknownCommand') {
const availableCommands = program.commands.map(cmd => cmd.name());
const suggestion = availableCommands.find(cmd =>
cmd.startsWith(err.message.split("'")[1]?.slice(0, 3) || '')
);
console.error(err.message);
if (suggestion) {
console.error(`Did you mean: ${suggestion}?`);
}
console.error('Run --help to see available commands');
}
process.exit(err.exitCode);
});
// Option suggestion configuration
program.showSuggestionAfterError(true); // Default behavior// Enhanced error context
const createEnhancedError = (message, context = {}) => {
const error = new InvalidArgumentError(message);
error.context = context;
return error;
};
// Validation with context
const validateConfigFile = (filepath) => {
try {
const config = JSON.parse(fs.readFileSync(filepath, 'utf8'));
return config;
} catch (parseError) {
throw createEnhancedError(
`Invalid JSON in config file: ${filepath}`,
{
parseError: parseError.message,
line: parseError.lineNumber,
column: parseError.columnNumber
}
);
}
};
// Debug-friendly error handling
program.exitOverride((err) => {
if (process.env.DEBUG) {
console.error('Error details:', {
code: err.code,
exitCode: err.exitCode,
stack: err.stack,
context: err.context
});
} else {
console.error(err.message);
}
process.exit(err.exitCode);
});// Validate dependencies before command execution
program
.hook('preAction', (thisCommand) => {
// Check required environment variables
const requiredEnvVars = ['API_KEY', 'DATABASE_URL'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
thisCommand.error(`Missing required environment variable: ${envVar}`, {
code: 'env.missing',
exitCode: 1
});
}
}
});// User-friendly error messages
const parseSize = (value) => {
const match = value.match(/^(\d+(?:\.\d+)?)(b|kb|mb|gb)$/i);
if (!match) {
throw new InvalidArgumentError(
`Invalid size format: ${value}. Use format like: 100b, 1.5kb, 2mb, 1gb`
);
}
const [, amount, unit] = match;
const multipliers = { b: 1, kb: 1024, mb: 1024**2, gb: 1024**3 };
return parseFloat(amount) * multipliers[unit.toLowerCase()];
};// Consistent error code naming
const ERROR_CODES = {
VALIDATION: {
INVALID_FORMAT: 'validation.format',
OUT_OF_RANGE: 'validation.range',
MISSING_REQUIRED: 'validation.required'
},
FILE: {
NOT_FOUND: 'file.notfound',
ACCESS_DENIED: 'file.access',
INVALID_FORMAT: 'file.format'
},
NETWORK: {
CONNECTION_FAILED: 'network.connection',
TIMEOUT: 'network.timeout',
UNAUTHORIZED: 'network.auth'
}
};
// Usage in validators
const validateUrl = (url) => {
try {
new URL(url);
return url;
} catch {
throw new CommanderError(1, ERROR_CODES.VALIDATION.INVALID_FORMAT, `Invalid URL: ${url}`);
}
};interface ErrorOptions {
/** Error code identifier for programmatic handling */
code?: string;
/** Suggested exit code for process termination */
exitCode?: number;
}