0
# Error Handling
1
2
Comprehensive error types for argument parsing and validation with "did you mean?" suggestions.
3
4
## Quick Reference
5
6
```typescript
7
// Safe error handling in commands (return Error instead of throwing)
8
func: async function(flags) {
9
try {
10
await operation();
11
} catch (err) {
12
return new Error(`Operation failed: ${err.message}`);
13
}
14
}
15
16
// Catch scanner errors
17
try {
18
await run(app, inputs, { process });
19
} catch (err) {
20
if (err instanceof ArgumentScannerError) {
21
// Handle parsing errors
22
console.error(err.message);
23
}
24
}
25
```
26
27
## ArgumentScannerError Hierarchy
28
29
All argument scanning errors extend `ArgumentScannerError`.
30
31
```typescript { .api }
32
abstract class ArgumentScannerError extends Error
33
```
34
35
### Error Types
36
37
| Error | When | Properties |
38
|-------|------|------------|
39
| `FlagNotFoundError` | Unknown flag name | `input`, `corrections`, `aliasName?` |
40
| `AliasNotFoundError` | Unknown alias letter | `input` |
41
| `ArgumentParseError` | Parser threw exception | `externalFlagNameOrPlaceholder`, `input`, `exception` |
42
| `EnumValidationError` | Invalid enum value | `externalFlagName`, `input`, `values`, `corrections` |
43
| `UnsatisfiedFlagError` | Missing required flag value | `externalFlagName`, `nextFlagName?` |
44
| `UnsatisfiedPositionalError` | Missing required argument | `placeholder`, `limit?` |
45
| `UnexpectedPositionalError` | Too many arguments | `expectedCount`, `input` |
46
| `UnexpectedFlagError` | Flag specified multiple times (non-variadic) | `externalFlagName`, `previousInput`, `input` |
47
| `InvalidNegatedFlagSyntaxError` | Value provided for negated flag | `externalFlagName`, `valueText` |
48
49
### Example Scenarios
50
51
```typescript
52
// FlagNotFoundError
53
// myapp --verbos
54
// Error: No flag registered for --verbos, did you mean --verbose?
55
56
// ArgumentParseError
57
// myapp --port abc
58
// Error: Failed to parse "abc" for port: Cannot convert abc to a number
59
60
// EnumValidationError
61
// myapp --env prodution
62
// Error: Expected "prodution" to be one of (dev|staging|prod), did you mean "prod"?
63
64
// UnsatisfiedFlagError
65
// myapp --output
66
// Error: Expected input for flag --output
67
68
// UnsatisfiedPositionalError
69
// myapp (expects 1 arg)
70
// Error: Expected argument for arg1
71
72
// UnexpectedPositionalError
73
// myapp file1 file2 file3 (expects max 2)
74
// Error: Too many arguments, expected 2 but encountered "file3"
75
```
76
77
## Custom Error Formatting
78
79
```typescript
80
import { formatMessageForArgumentScannerError } from "@stricli/core";
81
82
function handleError(error: ArgumentScannerError): string {
83
return formatMessageForArgumentScannerError(error, {
84
FlagNotFoundError: (err) => {
85
if (err.corrections.length > 0) {
86
return `Unknown flag: ${err.input}. Try: ${err.corrections.join(", ")}`;
87
}
88
return `Unknown flag: ${err.input}`;
89
},
90
ArgumentParseError: (err) => {
91
return `Invalid value "${err.input}" for ${err.externalFlagNameOrPlaceholder}`;
92
}
93
});
94
}
95
96
try {
97
await run(app, inputs, { process });
98
} catch (err) {
99
if (err instanceof ArgumentScannerError) {
100
const message = handleError(err);
101
console.error(message);
102
}
103
}
104
```
105
106
## Safe Error Handling in Commands
107
108
Commands can return `Error` instead of throwing for graceful error handling.
109
110
```typescript
111
const command = buildCommand({
112
func: async function(flags, filePath) {
113
try {
114
const content = await readFile(filePath);
115
this.process.stdout.write(content);
116
} catch (err) {
117
// Return Error - Stricli handles it gracefully
118
return new Error(`Failed to read file: ${err.message}`);
119
}
120
},
121
parameters: {
122
positional: {
123
kind: "tuple",
124
parameters: [{ brief: "File path", parse: String }]
125
}
126
},
127
docs: { brief: "Read file" }
128
});
129
```
130
131
## Complete Example
132
133
```typescript
134
import {
135
buildApplication,
136
buildCommand,
137
run,
138
ArgumentScannerError,
139
FlagNotFoundError,
140
ArgumentParseError,
141
formatMessageForArgumentScannerError
142
} from "@stricli/core";
143
144
const app = buildApplication(
145
buildCommand({
146
func: async function(flags) {
147
try {
148
// Operation
149
this.process.stdout.write("Success\n");
150
} catch (err) {
151
return new Error(`Operation failed: ${err.message}`);
152
}
153
},
154
parameters: {
155
flags: {
156
port: {
157
kind: "parsed",
158
parse: (input) => {
159
const port = parseInt(input, 10);
160
if (isNaN(port) || port < 1 || port > 65535) {
161
throw new Error("must be 1-65535");
162
}
163
return port;
164
},
165
brief: "Port"
166
}
167
}
168
},
169
docs: { brief: "Start server" }
170
}),
171
{ name: "server" }
172
);
173
174
try {
175
await run(app, process.argv.slice(2), { process });
176
} catch (err) {
177
if (err instanceof ArgumentScannerError) {
178
const message = formatMessageForArgumentScannerError(err, {
179
FlagNotFoundError: (e) =>
180
`ERROR: Flag "${e.input}" doesn't exist${
181
e.corrections.length > 0 ? `. Did you mean: ${e.corrections.join(", ")}?` : ""
182
}`,
183
ArgumentParseError: (e) =>
184
`ERROR: Invalid value "${e.input}" for --${e.externalFlagNameOrPlaceholder}. ${
185
e.exception instanceof Error ? e.exception.message : ""
186
}`
187
});
188
process.stderr.write(message + "\n");
189
process.exitCode = 1;
190
} else {
191
throw err;
192
}
193
}
194
```
195
196
## Related
197
198
- [Parameter Parsers](./parameter-parsers.md)
199
- [Exit Codes](./exit-codes.md)
200