CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-js-yaml

YAML 1.2 parser and serializer for JavaScript environments with complete specification support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

errors.mddocs/

Error Handling

js-yaml provides comprehensive error handling through the YAMLException class, which extends the standard JavaScript Error with detailed position information and helpful debugging features.

YAMLException Class

The primary error class for all YAML parsing and serialization errors.

class YAMLException extends Error {
  constructor(reason, mark);
  toString(compact);
}

Properties

interface YAMLException extends Error {
  name: 'YAMLException';
  reason: string;
  mark: Mark | null;
  message: string;
}

interface Mark {
  name: string | null;
  line: number;
  column: number;
  snippet: string | null;
}

Error Properties Details

name - Always 'YAMLException' for type identification

reason - Human-readable description of the error cause

mark - Position information where the error occurred:

  • name - Source filename (if provided in options)
  • line - Line number (0-based)
  • column - Column number (0-based)
  • snippet - Code snippet showing the error location

message - Formatted error message combining reason and position

Common Error Types

Parsing Errors

Invalid YAML Syntax:

try {
  yaml.load('invalid: yaml: syntax: error');
} catch (e) {
  console.log(e.reason); // "bad indentation of a mapping entry"
  console.log(e.mark.line); // 0
  console.log(e.mark.column); // 20
}

Duplicate Keys:

try {
  yaml.load(`
    key: value1  
    key: value2
  `);
} catch (e) {
  console.log(e.reason); // "duplicated mapping key"
}

Invalid Multi-Document:

// load() expects single document
try {
  yaml.load(`
    ---
    first: document
    ---
    second: document
  `);
} catch (e) {
  console.log(e.reason); // "expected a single document in the stream, but found more"
}

Type Resolution Errors

Unknown Tags:

try {
  yaml.load('value: !unknown-tag data');
} catch (e) {
  console.log(e.reason); // "unknown tag !<unknown-tag>"
}

Invalid Type Data:

try {
  yaml.load('timestamp: !!timestamp invalid-date');
} catch (e) {
  console.log(e.reason); // Cannot resolve timestamp
}

Serialization Errors

Non-serializable Values:

const objWithFunction = {
  name: 'test',
  callback: function() { return 'hello'; }
};

try {
  yaml.dump(objWithFunction);
} catch (e) {
  console.log(e.reason); // "unacceptable kind of an object to dump"
}

Circular References:

const circular = { name: 'circular' };
circular.self = circular;

try {
  yaml.dump(circular, { noRefs: true });
} catch (e) {
  console.log(e.reason); // "circular reference"
}

Error Handling Strategies

Basic Error Catching

const yaml = require('js-yaml');

function parseYamlSafely(yamlString) {
  try {
    return yaml.load(yamlString);
  } catch (e) {
    if (e instanceof yaml.YAMLException) {
      console.error('YAML parsing failed:', e.message);
      return null;
    }
    throw e; // Re-throw non-YAML errors
  }
}

Detailed Error Information

function parseWithDetailedErrors(yamlString, filename = null) {
  try {
    return yaml.load(yamlString, { filename });
  } catch (e) {
    if (e instanceof yaml.YAMLException) {
      console.error('YAML Error Details:');
      console.error('  Reason:', e.reason);
      
      if (e.mark) {
        console.error(`  Location: line ${e.mark.line + 1}, column ${e.mark.column + 1}`);
        
        if (e.mark.name) {
          console.error('  File:', e.mark.name);
        }
        
        if (e.mark.snippet) {
          console.error('  Snippet:');
          console.error(e.mark.snippet);
        }
      }
      return null;
    }
    throw e;
  }
}

Warning Handling

Handle non-fatal warnings during parsing:

function parseWithWarnings(yamlString) {
  const warnings = [];
  
  const doc = yaml.load(yamlString, {
    onWarning: (warning) => {
      warnings.push({
        message: warning.reason,
        line: warning.mark ? warning.mark.line + 1 : null,
        column: warning.mark ? warning.mark.column + 1 : null
      });
    }
  });
  
  return { document: doc, warnings };
}

// Usage
const result = parseWithWarnings(yamlContent);
if (result.warnings.length > 0) {
  console.warn(`Found ${result.warnings.length} warnings:`);
  result.warnings.forEach(w => {
    console.warn(`  Line ${w.line}: ${w.message}`);
  });
}

Error Recovery

Implement fallback strategies for failed parsing:

function parseWithFallback(yamlString, fallbackSchema = null) {
  const schemas = [
    yaml.DEFAULT_SCHEMA,
    yaml.JSON_SCHEMA,
    yaml.FAILSAFE_SCHEMA,
    fallbackSchema
  ].filter(Boolean);
  
  let lastError;
  
  for (const schema of schemas) {
    try {
      return yaml.load(yamlString, { 
        schema,
        skipInvalid: true 
      });
    } catch (e) {
      lastError = e;
      continue;
    }
  }
  
  throw lastError;
}

Validation with Error Aggregation

function validateYamlStructure(yamlString, requiredFields = []) {
  const errors = [];
  let doc;
  
  // Parse errors
  try {
    doc = yaml.load(yamlString);
  } catch (e) {
    if (e instanceof yaml.YAMLException) {
      errors.push({
        type: 'parse',
        message: e.reason,
        line: e.mark ? e.mark.line + 1 : null
      });
      return { valid: false, errors };
    }
    throw e;
  }
  
  // Structure validation errors
  if (typeof doc !== 'object' || doc === null) {
    errors.push({
      type: 'structure',
      message: 'Root document must be an object'
    });
  } else {
    requiredFields.forEach(field => {
      if (!(field in doc)) {
        errors.push({
          type: 'validation',
          message: `Missing required field: ${field}`
        });
      }
    });
  }
  
  return {
    valid: errors.length === 0,
    document: doc,
    errors
  };
}

Custom Error Types

Extend YAMLException for application-specific errors:

class ConfigValidationError extends yaml.YAMLException {
  constructor(message, field, mark = null) {
    super(message, mark);
    this.name = 'ConfigValidationError';
    this.field = field;
  }
}

function validateConfig(yamlString) {
  let config;
  
  try {
    config = yaml.load(yamlString);
  } catch (e) {
    throw e; // Re-throw parsing errors as-is
  }
  
  if (!config.database) {
    throw new ConfigValidationError(
      'Database configuration is required',
      'database'
    );
  }
  
  if (!config.database.host) {
    throw new ConfigValidationError(
      'Database host is required',
      'database.host'
    );
  }
  
  return config;
}

Error Formatting

Compact Error Display

function formatError(error, compact = false) {
  if (!(error instanceof yaml.YAMLException)) {
    return error.message;
  }
  
  if (compact) {
    return error.toString(true);
  }
  
  let formatted = `YAML Error: ${error.reason}`;
  
  if (error.mark) {
    formatted += `\nLocation: line ${error.mark.line + 1}, column ${error.mark.column + 1}`;
    
    if (error.mark.name) {
      formatted += ` in ${error.mark.name}`;
    }
    
    if (error.mark.snippet) {
      formatted += `\n\nSnippet:\n${error.mark.snippet}`;
    }
  }
  
  return formatted;
}

Error Logging

function logYamlError(error, context = {}) {
  const timestamp = new Date().toISOString();
  const logEntry = {
    timestamp,
    type: 'yaml_error',
    reason: error.reason,
    ...context
  };
  
  if (error.mark) {
    logEntry.position = {
      line: error.mark.line + 1,
      column: error.mark.column + 1,
      filename: error.mark.name
    };
  }
  
  console.error(JSON.stringify(logEntry, null, 2));
}

Testing Error Conditions

describe('YAML Error Handling', () => {
  test('should throw YAMLException for invalid syntax', () => {
    expect(() => {
      yaml.load('invalid: yaml: syntax');
    }).toThrow(yaml.YAMLException);
  });
  
  test('should provide position information', () => {
    try {
      yaml.load('key:\n  - invalid\n    syntax');
    } catch (e) {
      expect(e.mark.line).toBe(2);
      expect(e.mark.column).toBeGreaterThan(0);
      expect(e.mark.snippet).toContain('syntax');
    }
  });
  
  test('should handle warnings gracefully', () => {
    const warnings = [];
    const doc = yaml.load(yamlWithWarnings, {
      onWarning: (w) => warnings.push(w)
    });
    
    expect(warnings).toHaveLength(1);
    expect(warnings[0]).toBeInstanceOf(yaml.YAMLException);
  });
});

docs

dumping.md

errors.md

index.md

loading.md

schemas.md

tile.json