0
# Error Handling
1
2
js-yaml provides comprehensive error handling through the YAMLException class, which extends the standard JavaScript Error with detailed position information and helpful debugging features.
3
4
## YAMLException Class
5
6
The primary error class for all YAML parsing and serialization errors.
7
8
```javascript { .api }
9
class YAMLException extends Error {
10
constructor(reason, mark);
11
toString(compact);
12
}
13
```
14
15
### Properties
16
17
```javascript { .api }
18
interface YAMLException extends Error {
19
name: 'YAMLException';
20
reason: string;
21
mark: Mark | null;
22
message: string;
23
}
24
25
interface Mark {
26
name: string | null;
27
line: number;
28
column: number;
29
snippet: string | null;
30
}
31
```
32
33
### Error Properties Details
34
35
**name** - Always `'YAMLException'` for type identification
36
37
**reason** - Human-readable description of the error cause
38
39
**mark** - Position information where the error occurred:
40
- `name` - Source filename (if provided in options)
41
- `line` - Line number (0-based)
42
- `column` - Column number (0-based)
43
- `snippet` - Code snippet showing the error location
44
45
**message** - Formatted error message combining reason and position
46
47
## Common Error Types
48
49
### Parsing Errors
50
51
**Invalid YAML Syntax:**
52
53
```javascript
54
try {
55
yaml.load('invalid: yaml: syntax: error');
56
} catch (e) {
57
console.log(e.reason); // "bad indentation of a mapping entry"
58
console.log(e.mark.line); // 0
59
console.log(e.mark.column); // 20
60
}
61
```
62
63
**Duplicate Keys:**
64
65
```javascript
66
try {
67
yaml.load(`
68
key: value1
69
key: value2
70
`);
71
} catch (e) {
72
console.log(e.reason); // "duplicated mapping key"
73
}
74
```
75
76
**Invalid Multi-Document:**
77
78
```javascript
79
// load() expects single document
80
try {
81
yaml.load(`
82
---
83
first: document
84
---
85
second: document
86
`);
87
} catch (e) {
88
console.log(e.reason); // "expected a single document in the stream, but found more"
89
}
90
```
91
92
### Type Resolution Errors
93
94
**Unknown Tags:**
95
96
```javascript
97
try {
98
yaml.load('value: !unknown-tag data');
99
} catch (e) {
100
console.log(e.reason); // "unknown tag !<unknown-tag>"
101
}
102
```
103
104
**Invalid Type Data:**
105
106
```javascript
107
try {
108
yaml.load('timestamp: !!timestamp invalid-date');
109
} catch (e) {
110
console.log(e.reason); // Cannot resolve timestamp
111
}
112
```
113
114
### Serialization Errors
115
116
**Non-serializable Values:**
117
118
```javascript
119
const objWithFunction = {
120
name: 'test',
121
callback: function() { return 'hello'; }
122
};
123
124
try {
125
yaml.dump(objWithFunction);
126
} catch (e) {
127
console.log(e.reason); // "unacceptable kind of an object to dump"
128
}
129
```
130
131
**Circular References:**
132
133
```javascript
134
const circular = { name: 'circular' };
135
circular.self = circular;
136
137
try {
138
yaml.dump(circular, { noRefs: true });
139
} catch (e) {
140
console.log(e.reason); // "circular reference"
141
}
142
```
143
144
## Error Handling Strategies
145
146
### Basic Error Catching
147
148
```javascript
149
const yaml = require('js-yaml');
150
151
function parseYamlSafely(yamlString) {
152
try {
153
return yaml.load(yamlString);
154
} catch (e) {
155
if (e instanceof yaml.YAMLException) {
156
console.error('YAML parsing failed:', e.message);
157
return null;
158
}
159
throw e; // Re-throw non-YAML errors
160
}
161
}
162
```
163
164
### Detailed Error Information
165
166
```javascript
167
function parseWithDetailedErrors(yamlString, filename = null) {
168
try {
169
return yaml.load(yamlString, { filename });
170
} catch (e) {
171
if (e instanceof yaml.YAMLException) {
172
console.error('YAML Error Details:');
173
console.error(' Reason:', e.reason);
174
175
if (e.mark) {
176
console.error(` Location: line ${e.mark.line + 1}, column ${e.mark.column + 1}`);
177
178
if (e.mark.name) {
179
console.error(' File:', e.mark.name);
180
}
181
182
if (e.mark.snippet) {
183
console.error(' Snippet:');
184
console.error(e.mark.snippet);
185
}
186
}
187
return null;
188
}
189
throw e;
190
}
191
}
192
```
193
194
### Warning Handling
195
196
Handle non-fatal warnings during parsing:
197
198
```javascript
199
function parseWithWarnings(yamlString) {
200
const warnings = [];
201
202
const doc = yaml.load(yamlString, {
203
onWarning: (warning) => {
204
warnings.push({
205
message: warning.reason,
206
line: warning.mark ? warning.mark.line + 1 : null,
207
column: warning.mark ? warning.mark.column + 1 : null
208
});
209
}
210
});
211
212
return { document: doc, warnings };
213
}
214
215
// Usage
216
const result = parseWithWarnings(yamlContent);
217
if (result.warnings.length > 0) {
218
console.warn(`Found ${result.warnings.length} warnings:`);
219
result.warnings.forEach(w => {
220
console.warn(` Line ${w.line}: ${w.message}`);
221
});
222
}
223
```
224
225
### Error Recovery
226
227
Implement fallback strategies for failed parsing:
228
229
```javascript
230
function parseWithFallback(yamlString, fallbackSchema = null) {
231
const schemas = [
232
yaml.DEFAULT_SCHEMA,
233
yaml.JSON_SCHEMA,
234
yaml.FAILSAFE_SCHEMA,
235
fallbackSchema
236
].filter(Boolean);
237
238
let lastError;
239
240
for (const schema of schemas) {
241
try {
242
return yaml.load(yamlString, {
243
schema,
244
skipInvalid: true
245
});
246
} catch (e) {
247
lastError = e;
248
continue;
249
}
250
}
251
252
throw lastError;
253
}
254
```
255
256
### Validation with Error Aggregation
257
258
```javascript
259
function validateYamlStructure(yamlString, requiredFields = []) {
260
const errors = [];
261
let doc;
262
263
// Parse errors
264
try {
265
doc = yaml.load(yamlString);
266
} catch (e) {
267
if (e instanceof yaml.YAMLException) {
268
errors.push({
269
type: 'parse',
270
message: e.reason,
271
line: e.mark ? e.mark.line + 1 : null
272
});
273
return { valid: false, errors };
274
}
275
throw e;
276
}
277
278
// Structure validation errors
279
if (typeof doc !== 'object' || doc === null) {
280
errors.push({
281
type: 'structure',
282
message: 'Root document must be an object'
283
});
284
} else {
285
requiredFields.forEach(field => {
286
if (!(field in doc)) {
287
errors.push({
288
type: 'validation',
289
message: `Missing required field: ${field}`
290
});
291
}
292
});
293
}
294
295
return {
296
valid: errors.length === 0,
297
document: doc,
298
errors
299
};
300
}
301
```
302
303
## Custom Error Types
304
305
Extend YAMLException for application-specific errors:
306
307
```javascript
308
class ConfigValidationError extends yaml.YAMLException {
309
constructor(message, field, mark = null) {
310
super(message, mark);
311
this.name = 'ConfigValidationError';
312
this.field = field;
313
}
314
}
315
316
function validateConfig(yamlString) {
317
let config;
318
319
try {
320
config = yaml.load(yamlString);
321
} catch (e) {
322
throw e; // Re-throw parsing errors as-is
323
}
324
325
if (!config.database) {
326
throw new ConfigValidationError(
327
'Database configuration is required',
328
'database'
329
);
330
}
331
332
if (!config.database.host) {
333
throw new ConfigValidationError(
334
'Database host is required',
335
'database.host'
336
);
337
}
338
339
return config;
340
}
341
```
342
343
## Error Formatting
344
345
### Compact Error Display
346
347
```javascript
348
function formatError(error, compact = false) {
349
if (!(error instanceof yaml.YAMLException)) {
350
return error.message;
351
}
352
353
if (compact) {
354
return error.toString(true);
355
}
356
357
let formatted = `YAML Error: ${error.reason}`;
358
359
if (error.mark) {
360
formatted += `\nLocation: line ${error.mark.line + 1}, column ${error.mark.column + 1}`;
361
362
if (error.mark.name) {
363
formatted += ` in ${error.mark.name}`;
364
}
365
366
if (error.mark.snippet) {
367
formatted += `\n\nSnippet:\n${error.mark.snippet}`;
368
}
369
}
370
371
return formatted;
372
}
373
```
374
375
### Error Logging
376
377
```javascript
378
function logYamlError(error, context = {}) {
379
const timestamp = new Date().toISOString();
380
const logEntry = {
381
timestamp,
382
type: 'yaml_error',
383
reason: error.reason,
384
...context
385
};
386
387
if (error.mark) {
388
logEntry.position = {
389
line: error.mark.line + 1,
390
column: error.mark.column + 1,
391
filename: error.mark.name
392
};
393
}
394
395
console.error(JSON.stringify(logEntry, null, 2));
396
}
397
```
398
399
## Testing Error Conditions
400
401
```javascript
402
describe('YAML Error Handling', () => {
403
test('should throw YAMLException for invalid syntax', () => {
404
expect(() => {
405
yaml.load('invalid: yaml: syntax');
406
}).toThrow(yaml.YAMLException);
407
});
408
409
test('should provide position information', () => {
410
try {
411
yaml.load('key:\n - invalid\n syntax');
412
} catch (e) {
413
expect(e.mark.line).toBe(2);
414
expect(e.mark.column).toBeGreaterThan(0);
415
expect(e.mark.snippet).toContain('syntax');
416
}
417
});
418
419
test('should handle warnings gracefully', () => {
420
const warnings = [];
421
const doc = yaml.load(yamlWithWarnings, {
422
onWarning: (w) => warnings.push(w)
423
});
424
425
expect(warnings).toHaveLength(1);
426
expect(warnings[0]).toBeInstanceOf(yaml.YAMLException);
427
});
428
});
429
```