Custom error handling system with specific error types for parsing issues, validation failures, and comprehensive error information for debugging CSV parsing problems.
Custom error class extending native Error with CSV-specific error information and context.
/**
* Custom error class for CSV parsing errors
* @extends Error
*/
class CSVError extends Error {
/** Error message describing the issue */
err: string;
/** Line number where error occurred (zero-based) */
line: number;
/** Additional context information about the error */
extra?: string;
/**
* Creates a new CSV parsing error
* @param err - Error message
* @param line - Line number where error occurred
* @param extra - Optional additional error context
*/
constructor(err: string, line: number, extra?: string);
/**
* Convert error to JSON for serialization
* @returns Object with error details
*/
toJSON(): {err: string, line: number, extra?: string};
}Usage Examples:
import csvtojson from "csvtojson";
try {
const jsonArray = await csvtojson({
checkColumn: true,
maxRowLength: 1000
})
.fromString('name,age,city\nAlice,25\nBob,30,London,Extra');
} catch (error) {
if (error instanceof CSVError) {
console.log('CSV Error:', error.err);
console.log('Line:', error.line);
if (error.extra) {
console.log('Context:', error.extra);
}
// Serialize error
const errorData = error.toJSON();
console.log('Error data:', errorData);
}
}Predefined static methods for creating specific types of CSV errors.
/**
* Create error for column count mismatch
* @param index - Line index where mismatch occurred
* @param extra - Optional additional context
* @returns CSVError instance
*/
static column_mismatched(index: number, extra?: string): CSVError;
/**
* Create error for unclosed quote
* @param index - Line index where unclosed quote occurred
* @param extra - Optional additional context
* @returns CSVError instance
*/
static unclosed_quote(index: number, extra?: string): CSVError;
/**
* Create CSVError from JSON object (for deserialization)
* @param obj - Object with err, line, and optional extra properties
* @returns CSVError instance
*/
static fromJSON(obj: {err: string, line: number, extra?: string}): CSVError;Usage Examples:
import csvtojson, { CSVError } from "csvtojson";
// Handle different error types
csvtojson({
checkColumn: true,
quote: '"'
})
.fromFile('./problematic-data.csv')
.subscribe(
(jsonObj) => {
console.log('Parsed:', jsonObj);
},
(error) => {
if (error instanceof CSVError) {
switch (error.err) {
case 'column_mismatched':
console.error(`Column count mismatch at line ${error.line + 1}`);
if (error.extra) {
console.error(`Expected columns but got: ${error.extra}`);
}
break;
case 'unclosed_quote':
console.error(`Unclosed quote at line ${error.line + 1}`);
if (error.extra) {
console.error(`Near: ${error.extra}`);
}
break;
default:
console.error(`Parsing error: ${error.err} at line ${error.line + 1}`);
}
}
}
);Comprehensive error information for debugging complex parsing issues.
interface CSVError extends Error {
/** The specific error type identifier */
err: 'column_mismatched' | 'unclosed_quote' | 'row_exceed' | string;
/** Zero-based line number where error occurred */
line: number;
/** Additional context like partial data or expected vs actual values */
extra?: string;
}Usage Examples:
import csvtojson from "csvtojson";
async function parseWithDetailedErrorHandling(filePath: string) {
try {
const result = await csvtojson({
checkColumn: true,
maxRowLength: 10000,
quote: '"'
})
.fromFile(filePath);
return result;
} catch (error) {
if (error instanceof CSVError) {
// Create detailed error report
const errorReport = {
type: error.err,
lineNumber: error.line + 1, // Convert to 1-based
message: error.message,
context: error.extra,
file: filePath,
timestamp: new Date().toISOString()
};
// Log structured error
console.error('CSV Parsing Error:', JSON.stringify(errorReport, null, 2));
// Handle specific error types
switch (error.err) {
case 'column_mismatched':
console.error('Suggestion: Check if delimiters are consistent');
break;
case 'unclosed_quote':
console.error('Suggestion: Check for unescaped quotes in data');
break;
case 'row_exceed':
console.error('Suggestion: Increase maxRowLength or check for corrupted data');
break;
}
throw errorReport;
}
// Re-throw non-CSV errors
throw error;
}
}Strategies for handling and recovering from parsing errors in production.
/**
* Error handling patterns for robust CSV processing
*/
interface ErrorRecoveryOptions {
/** Continue processing after errors */
continueOnError?: boolean;
/** Maximum errors before aborting */
maxErrors?: number;
/** Callback for handling individual errors */
onError?: (error: CSVError) => boolean; // Return true to continue
}Usage Examples:
import csvtojson, { CSVError } from "csvtojson";
// Graceful error handling with recovery
async function robustCSVParsing(filePath: string) {
const results: any[] = [];
const errors: CSVError[] = [];
let errorCount = 0;
const maxErrors = 10;
return new Promise((resolve, reject) => {
csvtojson({
checkColumn: false, // More lenient parsing
ignoreEmpty: true, // Skip empty rows
maxRowLength: 100000
})
.fromFile(filePath)
.subscribe(
(jsonObj, lineNumber) => {
try {
// Validate data before adding
if (isValidRecord(jsonObj)) {
results.push(jsonObj);
} else {
console.warn(`Invalid record at line ${lineNumber}:`, jsonObj);
}
} catch (validationError) {
console.error(`Validation error at line ${lineNumber}:`, validationError);
}
},
(csvError) => {
errorCount++;
errors.push(csvError);
console.error(`Error ${errorCount}/${maxErrors} at line ${csvError.line + 1}: ${csvError.err}`);
if (errorCount >= maxErrors) {
reject(new Error(`Too many errors (${errorCount}). Aborting.`));
}
},
() => {
// Parsing completed
console.log(`Parsing completed with ${results.length} records and ${errorCount} errors`);
resolve({
data: results,
errors: errors,
stats: {
totalRecords: results.length,
errorCount: errorCount,
successRate: (results.length / (results.length + errorCount)) * 100
}
});
}
);
});
}
function isValidRecord(record: any): boolean {
// Implement your validation logic
return record && typeof record === 'object' && Object.keys(record).length > 0;
}Handle errors in streaming scenarios with proper cleanup and resource management.
/**
* Stream-specific error handling for large file processing
*/
interface StreamErrorHandling {
/** Handle stream errors without stopping processing */
onStreamError?: (error: Error) => void;
/** Handle individual record errors */
onRecordError?: (error: CSVError, record: any) => boolean;
}Usage Examples:
import csvtojson from "csvtojson";
import { createWriteStream } from "fs";
// Streaming with error handling and logging
function processLargeCSVWithErrorLogging(inputFile: string, outputFile: string) {
const outputStream = createWriteStream(outputFile);
const errorLogStream = createWriteStream(`${outputFile}.errors.log`);
let processedCount = 0;
let errorCount = 0;
csvtojson({
checkType: true,
trim: true
})
.fromFile(inputFile)
.subscribe(
async (jsonObj, lineNumber) => {
try {
// Process and validate data
const processedObj = await processRecord(jsonObj);
// Write to output
outputStream.write(JSON.stringify(processedObj) + '\n');
processedCount++;
if (processedCount % 1000 === 0) {
console.log(`Processed ${processedCount} records`);
}
} catch (processingError) {
errorCount++;
const errorEntry = {
line: lineNumber + 1,
error: processingError.message,
data: jsonObj,
timestamp: new Date().toISOString()
};
errorLogStream.write(JSON.stringify(errorEntry) + '\n');
}
},
(csvError) => {
// Handle CSV parsing errors
errorCount++;
const errorEntry = {
line: csvError.line + 1,
type: 'csv_parse_error',
error: csvError.err,
extra: csvError.extra,
timestamp: new Date().toISOString()
};
errorLogStream.write(JSON.stringify(errorEntry) + '\n');
console.error(`CSV parsing error at line ${csvError.line + 1}: ${csvError.err}`);
},
() => {
// Cleanup and final reporting
outputStream.end();
errorLogStream.end();
console.log(`\nProcessing completed:`);
console.log(`- Processed records: ${processedCount}`);
console.log(`- Errors: ${errorCount}`);
console.log(`- Success rate: ${((processedCount / (processedCount + errorCount)) * 100).toFixed(2)}%`);
}
);
}
async function processRecord(record: any): Promise<any> {
// Implement your record processing logic
// Throw errors for invalid records
if (!record.email || !record.email.includes('@')) {
throw new Error('Invalid email address');
}
return {
...record,
processed_at: new Date().toISOString()
};
}