CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-oas-normalize

Tooling for converting, validating, and parsing OpenAPI, Swagger, and Postman API definitions

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Custom error classes and utility functions for managing validation errors, compilation, and error reporting with contextual information.

Capabilities

ValidationError Class

Custom error class that extends the standard Error class, specifically designed for API validation failures.

/**
 * Custom error class for validation failures
 * Extends standard Error with specific name for validation contexts
 */
class ValidationError extends Error {
  constructor(message: string);
  name: 'ValidationError';
}

Usage Examples:

import { ValidationError } from "oas-normalize/lib/errors";

// Catching validation errors
const oas = new OASNormalize(invalidSpec);

try {
  await oas.validate();
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message);
    console.log('Error type:', error.name); // "ValidationError"
    console.log('Stack trace:', error.stack);
  } else {
    console.error('Unexpected error:', error);
  }
}
// Custom validation error handling
function handleAPIValidation(spec: any) {
  return new OASNormalize(spec)
    .validate()
    .catch(error => {
      if (error instanceof ValidationError) {
        // Handle validation-specific errors
        return {
          success: false,
          validationError: true,
          message: error.message,
          details: parseValidationMessage(error.message)
        };
      }
      
      // Re-throw other errors
      throw error;
    });
}

Compile Errors Function

Utility function for compiling validation results into human-readable error messages with contextual information.

/**
 * Compile validation errors into readable format with line numbers and context
 * Re-exported from @readme/openapi-parser for convenience
 * @param result - Validation result containing errors and warnings
 * @returns Formatted error message string
 */
function compileErrors(result: ValidationResult): string;

interface ValidationResult {
  valid: boolean;
  errors: ErrorDetails[];
  warnings: WarningDetails[];
}

interface ErrorDetails {
  message: string;
  path: string;
  location?: {
    line: number;
    column: number;
  };
}

Usage Examples:

import { compileErrors } from "oas-normalize/lib/utils";
import { validate } from "@readme/openapi-parser";

// Manual validation and error compilation
const spec = {
  openapi: "3.0.0",
  info: { title: "API" }, // Missing version
  paths: {}
};

const result = await validate(spec, { validate: { errors: { colorize: false } } });

if (!result.valid) {
  const errorMessage = compileErrors(result);
  console.error('Validation failed:\n', errorMessage);
  
  // Output:
  // OpenAPI schema validation failed.
  // 
  // REQUIRED must have required property 'version'
  // 
  //    2 |   "info": {
  //  > 3 |     "title": "API"
  //      |     ^ ☹️  version is missing here!
  //    4 |   },
}

Error Types and Scenarios

Validation Errors

Most common errors thrown by OAS Normalize are validation-related:

// Schema validation errors
const invalidSchema = {
  openapi: "3.0.0",
  info: { title: "API" }, // Missing required version
  paths: {
    "/pets/{id}": {
      get: {
        parameters: [
          {
            name: "filter", // Wrong parameter name (should be "id")
            in: "query"
          }
        ]
      }
    }
  }
};

try {
  await new OASNormalize(invalidSchema).validate();
} catch (error) {
  console.log(error instanceof ValidationError); // true
  console.log(error.name); // "ValidationError"
  // Error message includes detailed location information
}

Unsupported Format Errors

const unsupportedFormat = {
  // No swagger, openapi, or postman collection indicators
  api: "1.0", 
  title: "Some API"
};

try {
  await new OASNormalize(unsupportedFormat).validate();
} catch (error) {
  console.log(error instanceof ValidationError); // true
  console.log(error.message); // "The supplied API definition is unsupported."
}

Legacy Version Errors

const swagger12 = {
  swagger: "1.2", // Unsupported legacy version
  info: { title: "Old API" }
};

try {
  await new OASNormalize(swagger12).validate();
} catch (error) {
  console.log(error instanceof ValidationError); // true
  console.log(error.message); // "Swagger v1.2 is unsupported."
}

File Access Errors

// Security-related errors
const specWithLocalRef = {
  openapi: "3.0.0",
  info: { title: "API", version: "1.0.0" },
  paths: {
    "/test": {
      get: {
        parameters: [{
          schema: { $ref: "/etc/passwd" } // System file access attempt
        }]
      }
    }
  }
};

try {
  await new OASNormalize(specWithLocalRef).validate();
} catch (error) {
  console.log(error.message); // Contains reference resolution error
}

// Path access without enablePaths
try {
  await new OASNormalize("./local-file.yaml").load();
} catch (error) {
  console.log(error.message); // "Use `opts.enablePaths` to enable accessing local files."
}

Error Message Formatting

Colorized Output

Error messages can include ANSI color codes for terminal display:

const oas = new OASNormalize(invalidSpec, { colorizeErrors: true });

try {
  await oas.validate();
} catch (error) {
  // Error message includes color codes for:
  // - Error locations (red)
  // - Line numbers (cyan)
  // - Error indicators (red)
  // - Context lines (gray)
  console.error(error.message);
}

Plain Text Output

Default error messages are plain text without color codes:

const oas = new OASNormalize(invalidSpec, { colorizeErrors: false });

try {
  await oas.validate();
} catch (error) {
  // Plain text output suitable for logging or non-terminal environments
  console.error(error.message);
}

Error Handling Patterns

Comprehensive Error Handling

async function validateAPISpec(spec: any, options: Options = {}) {
  try {
    const oas = new OASNormalize(spec, options);
    const result = await oas.validate();
    
    return {
      success: true,
      warnings: result.warnings,
      spec: await oas.convert() // Return normalized spec
    };
    
  } catch (error) {
    if (error instanceof ValidationError) {
      return {
        success: false,
        type: 'validation',
        message: error.message,
        details: parseErrorDetails(error.message)
      };
    }
    
    // Handle other error types
    return {
      success: false,
      type: 'system',
      message: error.message,
      error: error
    };
  }
}

function parseErrorDetails(message: string) {
  // Extract structured information from error message
  const lines = message.split('\n');
  const errors = [];
  
  // Parse contextual error information
  // Implementation depends on specific error format needs
  
  return errors;
}

Retry Logic with Error Classification

async function validateWithRetry(spec: any, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const oas = new OASNormalize(spec);
      return await oas.validate();
      
    } catch (error) {
      if (error instanceof ValidationError) {
        // Don't retry validation errors - they won't change
        throw error;
      }
      
      if (attempt === maxRetries) {
        throw new Error(`Failed after ${maxRetries} attempts: ${error.message}`);
      }
      
      // Wait before retry (exponential backoff)
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
    }
  }
}

Batch Processing with Error Aggregation

async function validateMultipleSpecs(specs: any[]) {
  const results = await Promise.allSettled(
    specs.map((spec, index) => 
      new OASNormalize(spec)
        .validate()
        .then(result => ({ index, success: true, result }))
        .catch(error => ({ 
          index, 
          success: false, 
          error: error instanceof ValidationError ? error : new Error(error.message)
        }))
    )
  );
  
  const successful = results.filter((r): r is PromiseFulfilledResult<any> => 
    r.status === 'fulfilled' && r.value.success
  );
  
  const failed = results.filter((r): r is PromiseFulfilledResult<any> => 
    r.status === 'fulfilled' && !r.value.success
  );
  
  return {
    successful: successful.map(r => r.value),
    failed: failed.map(r => ({
      index: r.value.index,
      error: r.value.error,
      isValidationError: r.value.error instanceof ValidationError
    })),
    summary: {
      total: specs.length,
      successful: successful.length,
      failed: failed.length
    }
  };
}

Integration with Logging

Structured Logging

import { ValidationError } from "oas-normalize/lib/errors";

class APIValidator {
  private logger: Logger;
  
  async validateSpec(spec: any, context: { source: string; version?: string } = {}) {
    try {
      const oas = new OASNormalize(spec);
      const result = await oas.validate();
      
      this.logger.info('API validation successful', {
        source: context.source,
        version: context.version,
        warnings: result.warnings.length
      });
      
      return result;
      
    } catch (error) {
      if (error instanceof ValidationError) {
        this.logger.error('API validation failed', {
          source: context.source,
          version: context.version,
          error: error.message,
          type: 'validation'
        });
      } else {
        this.logger.error('API validation system error', {
          source: context.source,
          error: error.message,
          type: 'system'
        });
      }
      
      throw error;
    }
  }
}

Error Reporting

function createErrorReport(error: ValidationError) {
  return {
    timestamp: new Date().toISOString(),
    type: 'validation_error',
    name: error.name,
    message: error.message,
    stack: error.stack,
    parsed: {
      errors: extractErrorLocations(error.message),
      summary: extractErrorSummary(error.message)
    }
  };
}

function extractErrorLocations(message: string) {
  // Parse line number and location information from formatted error message
  const locationRegex = />\s*(\d+)\s*\|/g;
  const locations = [];
  let match;
  
  while ((match = locationRegex.exec(message)) !== null) {
    locations.push(parseInt(match[1]));
  }
  
  return locations;
}

Install with Tessl CLI

npx tessl i tessl/npm-oas-normalize

docs

core-processing.md

error-handling.md

format-conversion.md

index.md

reference-resolution.md

utility-functions.md

validation.md

tile.json