0
# Error Handling
1
2
Custom error classes and utility functions for managing validation errors, compilation, and error reporting with contextual information.
3
4
## Capabilities
5
6
### ValidationError Class
7
8
Custom error class that extends the standard Error class, specifically designed for API validation failures.
9
10
```typescript { .api }
11
/**
12
* Custom error class for validation failures
13
* Extends standard Error with specific name for validation contexts
14
*/
15
class ValidationError extends Error {
16
constructor(message: string);
17
name: 'ValidationError';
18
}
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import { ValidationError } from "oas-normalize/lib/errors";
25
26
// Catching validation errors
27
const oas = new OASNormalize(invalidSpec);
28
29
try {
30
await oas.validate();
31
} catch (error) {
32
if (error instanceof ValidationError) {
33
console.error('Validation failed:', error.message);
34
console.log('Error type:', error.name); // "ValidationError"
35
console.log('Stack trace:', error.stack);
36
} else {
37
console.error('Unexpected error:', error);
38
}
39
}
40
```
41
42
```typescript
43
// Custom validation error handling
44
function handleAPIValidation(spec: any) {
45
return new OASNormalize(spec)
46
.validate()
47
.catch(error => {
48
if (error instanceof ValidationError) {
49
// Handle validation-specific errors
50
return {
51
success: false,
52
validationError: true,
53
message: error.message,
54
details: parseValidationMessage(error.message)
55
};
56
}
57
58
// Re-throw other errors
59
throw error;
60
});
61
}
62
```
63
64
### Compile Errors Function
65
66
Utility function for compiling validation results into human-readable error messages with contextual information.
67
68
```typescript { .api }
69
/**
70
* Compile validation errors into readable format with line numbers and context
71
* Re-exported from @readme/openapi-parser for convenience
72
* @param result - Validation result containing errors and warnings
73
* @returns Formatted error message string
74
*/
75
function compileErrors(result: ValidationResult): string;
76
77
interface ValidationResult {
78
valid: boolean;
79
errors: ErrorDetails[];
80
warnings: WarningDetails[];
81
}
82
83
interface ErrorDetails {
84
message: string;
85
path: string;
86
location?: {
87
line: number;
88
column: number;
89
};
90
}
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import { compileErrors } from "oas-normalize/lib/utils";
97
import { validate } from "@readme/openapi-parser";
98
99
// Manual validation and error compilation
100
const spec = {
101
openapi: "3.0.0",
102
info: { title: "API" }, // Missing version
103
paths: {}
104
};
105
106
const result = await validate(spec, { validate: { errors: { colorize: false } } });
107
108
if (!result.valid) {
109
const errorMessage = compileErrors(result);
110
console.error('Validation failed:\n', errorMessage);
111
112
// Output:
113
// OpenAPI schema validation failed.
114
//
115
// REQUIRED must have required property 'version'
116
//
117
// 2 | "info": {
118
// > 3 | "title": "API"
119
// | ^ ☹️ version is missing here!
120
// 4 | },
121
}
122
```
123
124
## Error Types and Scenarios
125
126
### Validation Errors
127
128
Most common errors thrown by OAS Normalize are validation-related:
129
130
```typescript
131
// Schema validation errors
132
const invalidSchema = {
133
openapi: "3.0.0",
134
info: { title: "API" }, // Missing required version
135
paths: {
136
"/pets/{id}": {
137
get: {
138
parameters: [
139
{
140
name: "filter", // Wrong parameter name (should be "id")
141
in: "query"
142
}
143
]
144
}
145
}
146
}
147
};
148
149
try {
150
await new OASNormalize(invalidSchema).validate();
151
} catch (error) {
152
console.log(error instanceof ValidationError); // true
153
console.log(error.name); // "ValidationError"
154
// Error message includes detailed location information
155
}
156
```
157
158
### Unsupported Format Errors
159
160
```typescript
161
const unsupportedFormat = {
162
// No swagger, openapi, or postman collection indicators
163
api: "1.0",
164
title: "Some API"
165
};
166
167
try {
168
await new OASNormalize(unsupportedFormat).validate();
169
} catch (error) {
170
console.log(error instanceof ValidationError); // true
171
console.log(error.message); // "The supplied API definition is unsupported."
172
}
173
```
174
175
### Legacy Version Errors
176
177
```typescript
178
const swagger12 = {
179
swagger: "1.2", // Unsupported legacy version
180
info: { title: "Old API" }
181
};
182
183
try {
184
await new OASNormalize(swagger12).validate();
185
} catch (error) {
186
console.log(error instanceof ValidationError); // true
187
console.log(error.message); // "Swagger v1.2 is unsupported."
188
}
189
```
190
191
### File Access Errors
192
193
```typescript
194
// Security-related errors
195
const specWithLocalRef = {
196
openapi: "3.0.0",
197
info: { title: "API", version: "1.0.0" },
198
paths: {
199
"/test": {
200
get: {
201
parameters: [{
202
schema: { $ref: "/etc/passwd" } // System file access attempt
203
}]
204
}
205
}
206
}
207
};
208
209
try {
210
await new OASNormalize(specWithLocalRef).validate();
211
} catch (error) {
212
console.log(error.message); // Contains reference resolution error
213
}
214
215
// Path access without enablePaths
216
try {
217
await new OASNormalize("./local-file.yaml").load();
218
} catch (error) {
219
console.log(error.message); // "Use `opts.enablePaths` to enable accessing local files."
220
}
221
```
222
223
## Error Message Formatting
224
225
### Colorized Output
226
227
Error messages can include ANSI color codes for terminal display:
228
229
```typescript
230
const oas = new OASNormalize(invalidSpec, { colorizeErrors: true });
231
232
try {
233
await oas.validate();
234
} catch (error) {
235
// Error message includes color codes for:
236
// - Error locations (red)
237
// - Line numbers (cyan)
238
// - Error indicators (red)
239
// - Context lines (gray)
240
console.error(error.message);
241
}
242
```
243
244
### Plain Text Output
245
246
Default error messages are plain text without color codes:
247
248
```typescript
249
const oas = new OASNormalize(invalidSpec, { colorizeErrors: false });
250
251
try {
252
await oas.validate();
253
} catch (error) {
254
// Plain text output suitable for logging or non-terminal environments
255
console.error(error.message);
256
}
257
```
258
259
## Error Handling Patterns
260
261
### Comprehensive Error Handling
262
263
```typescript
264
async function validateAPISpec(spec: any, options: Options = {}) {
265
try {
266
const oas = new OASNormalize(spec, options);
267
const result = await oas.validate();
268
269
return {
270
success: true,
271
warnings: result.warnings,
272
spec: await oas.convert() // Return normalized spec
273
};
274
275
} catch (error) {
276
if (error instanceof ValidationError) {
277
return {
278
success: false,
279
type: 'validation',
280
message: error.message,
281
details: parseErrorDetails(error.message)
282
};
283
}
284
285
// Handle other error types
286
return {
287
success: false,
288
type: 'system',
289
message: error.message,
290
error: error
291
};
292
}
293
}
294
295
function parseErrorDetails(message: string) {
296
// Extract structured information from error message
297
const lines = message.split('\n');
298
const errors = [];
299
300
// Parse contextual error information
301
// Implementation depends on specific error format needs
302
303
return errors;
304
}
305
```
306
307
### Retry Logic with Error Classification
308
309
```typescript
310
async function validateWithRetry(spec: any, maxRetries = 3) {
311
for (let attempt = 1; attempt <= maxRetries; attempt++) {
312
try {
313
const oas = new OASNormalize(spec);
314
return await oas.validate();
315
316
} catch (error) {
317
if (error instanceof ValidationError) {
318
// Don't retry validation errors - they won't change
319
throw error;
320
}
321
322
if (attempt === maxRetries) {
323
throw new Error(`Failed after ${maxRetries} attempts: ${error.message}`);
324
}
325
326
// Wait before retry (exponential backoff)
327
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
328
}
329
}
330
}
331
```
332
333
### Batch Processing with Error Aggregation
334
335
```typescript
336
async function validateMultipleSpecs(specs: any[]) {
337
const results = await Promise.allSettled(
338
specs.map((spec, index) =>
339
new OASNormalize(spec)
340
.validate()
341
.then(result => ({ index, success: true, result }))
342
.catch(error => ({
343
index,
344
success: false,
345
error: error instanceof ValidationError ? error : new Error(error.message)
346
}))
347
)
348
);
349
350
const successful = results.filter((r): r is PromiseFulfilledResult<any> =>
351
r.status === 'fulfilled' && r.value.success
352
);
353
354
const failed = results.filter((r): r is PromiseFulfilledResult<any> =>
355
r.status === 'fulfilled' && !r.value.success
356
);
357
358
return {
359
successful: successful.map(r => r.value),
360
failed: failed.map(r => ({
361
index: r.value.index,
362
error: r.value.error,
363
isValidationError: r.value.error instanceof ValidationError
364
})),
365
summary: {
366
total: specs.length,
367
successful: successful.length,
368
failed: failed.length
369
}
370
};
371
}
372
```
373
374
## Integration with Logging
375
376
### Structured Logging
377
378
```typescript
379
import { ValidationError } from "oas-normalize/lib/errors";
380
381
class APIValidator {
382
private logger: Logger;
383
384
async validateSpec(spec: any, context: { source: string; version?: string } = {}) {
385
try {
386
const oas = new OASNormalize(spec);
387
const result = await oas.validate();
388
389
this.logger.info('API validation successful', {
390
source: context.source,
391
version: context.version,
392
warnings: result.warnings.length
393
});
394
395
return result;
396
397
} catch (error) {
398
if (error instanceof ValidationError) {
399
this.logger.error('API validation failed', {
400
source: context.source,
401
version: context.version,
402
error: error.message,
403
type: 'validation'
404
});
405
} else {
406
this.logger.error('API validation system error', {
407
source: context.source,
408
error: error.message,
409
type: 'system'
410
});
411
}
412
413
throw error;
414
}
415
}
416
}
417
```
418
419
### Error Reporting
420
421
```typescript
422
function createErrorReport(error: ValidationError) {
423
return {
424
timestamp: new Date().toISOString(),
425
type: 'validation_error',
426
name: error.name,
427
message: error.message,
428
stack: error.stack,
429
parsed: {
430
errors: extractErrorLocations(error.message),
431
summary: extractErrorSummary(error.message)
432
}
433
};
434
}
435
436
function extractErrorLocations(message: string) {
437
// Parse line number and location information from formatted error message
438
const locationRegex = />\s*(\d+)\s*\|/g;
439
const locations = [];
440
let match;
441
442
while ((match = locationRegex.exec(message)) !== null) {
443
locations.push(parseInt(match[1]));
444
}
445
446
return locations;
447
}
448
```