CSV Parse provides comprehensive error handling with specific error codes, detailed context information, and flexible error recovery options for robust CSV processing.
Custom error class extending JavaScript's built-in Error with CSV-specific information and context.
/**
* Custom error class for CSV parsing errors
*/
class CsvError extends Error {
/** Specific error code identifying the type of parsing error */
readonly code: CsvErrorCode;
/** Additional context properties based on error type */
[key: string]: any;
/**
* Creates a new CSV parsing error
* @param code - Specific error code
* @param message - Error message or array of message parts
* @param options - Parser options for context
* @param contexts - Additional context objects
*/
constructor(
code: CsvErrorCode,
message: string | string[],
options?: OptionsNormalized,
...contexts: unknown[]
);
}Usage Examples:
import { parse, CsvError } from "csv-parse";
try {
parse("invalid,csv,data\nwith,inconsistent", {
columns: true,
relax_column_count: false
});
} catch (err) {
if (err instanceof CsvError) {
console.error("CSV Error Code:", err.code);
console.error("Message:", err.message);
console.error("Line:", err.lines);
console.error("Column:", err.column);
console.error("Raw record:", err.raw);
}
}
// Error context extraction
function handleCsvError(error) {
if (error instanceof CsvError) {
const context = {
code: error.code,
message: error.message,
line: error.lines || 'unknown',
column: error.column || 'unknown',
rawData: error.raw || 'unavailable'
};
console.error("CSV parsing failed:", context);
return context;
}
throw error; // Re-throw non-CSV errors
}Comprehensive set of error codes identifying specific parsing issues.
type CsvErrorCode =
// Argument and validation errors
| "CSV_INVALID_ARGUMENT"
| "CSV_INVALID_COLUMN_DEFINITION"
| "CSV_INVALID_COLUMN_MAPPING"
// Option validation errors
| "CSV_INVALID_OPTION_BOM"
| "CSV_INVALID_OPTION_CAST"
| "CSV_INVALID_OPTION_CAST_DATE"
| "CSV_INVALID_OPTION_COLUMNS"
| "CSV_INVALID_OPTION_COMMENT"
| "CSV_INVALID_OPTION_DELIMITER"
| "CSV_INVALID_OPTION_GROUP_COLUMNS_BY_NAME"
| "CSV_INVALID_OPTION_ON_RECORD"
// Parsing format errors
| "CSV_INVALID_CLOSING_QUOTE"
| "CSV_QUOTE_NOT_CLOSED"
| "INVALID_OPENING_QUOTE"
| "CSV_NON_TRIMABLE_CHAR_AFTER_CLOSING_QUOTE"
// Record structure errors
| "CSV_RECORD_INCONSISTENT_FIELDS_LENGTH"
| "CSV_RECORD_INCONSISTENT_COLUMNS"
| "CSV_OPTION_COLUMNS_MISSING_NAME"
// Resource and limit errors
| "CSV_MAX_RECORD_SIZE"
// General errors
| "CSV_UNKNOWN_ERROR";Usage Examples:
import { parse, CsvError } from "csv-parse";
// Handle specific error types
try {
parse(data, options);
} catch (err) {
if (err instanceof CsvError) {
switch (err.code) {
case "CSV_INVALID_CLOSING_QUOTE":
console.error("Quote not properly closed at line", err.lines);
break;
case "CSV_RECORD_INCONSISTENT_FIELDS_LENGTH":
console.error("Inconsistent column count at line", err.lines);
console.error("Expected", err.expectedColumns, "but got", err.actualColumns);
break;
case "CSV_MAX_RECORD_SIZE":
console.error("Record too large at line", err.lines);
console.error("Maximum size:", err.maxSize, "bytes");
break;
case "CSV_INVALID_OPTION_DELIMITER":
console.error("Invalid delimiter configuration:", err.delimiter);
break;
default:
console.error("Unknown CSV error:", err.code, err.message);
}
}
}Options and patterns for handling and recovering from parsing errors.
Usage Examples:
import { parse } from "csv-parse";
// Skip invalid records and continue processing
const parser = parse({
columns: true,
skip_records_with_error: true,
relax_column_count: true,
on_skip: (err, rawRecord) => {
// Log skipped records for later analysis
console.warn(`Skipped record at line ${err?.lines || 'unknown'}: ${rawRecord}`);
console.warn(`Reason: ${err?.message || 'unknown error'}`);
// Optionally store for later processing
skippedRecords.push({
line: err?.lines,
raw: rawRecord,
error: err?.code,
message: err?.message
});
}
});
// Collect parsing statistics
let totalRecords = 0;
let errorCount = 0;
const skippedRecords = [];
parser.on('data', (record) => {
totalRecords++;
});
parser.on('end', () => {
console.log(`Processing complete:`);
console.log(`- Total records: ${totalRecords}`);
console.log(`- Skipped records: ${skippedRecords.length}`);
console.log(`- Success rate: ${((totalRecords / (totalRecords + skippedRecords.length)) * 100).toFixed(2)}%`);
});Proactive error prevention through validation and safe parsing practices.
Usage Examples:
import { parse, CsvError } from "csv-parse";
// Validate options before parsing
function validateCsvOptions(options) {
try {
// Test with minimal data to validate options
parse("test", options);
return { valid: true };
} catch (err) {
if (err instanceof CsvError && err.code.startsWith('CSV_INVALID_OPTION')) {
return {
valid: false,
error: err.code,
message: err.message
};
}
return { valid: true }; // Other errors are not option-related
}
}
// Safe parsing wrapper
async function safeCsvParse(data, options = {}) {
const safeOptions = {
// Default safe options
skip_records_with_error: true,
relax_column_count: true,
relax_quotes: true,
skip_empty_lines: true,
max_record_size: 1024 * 1024, // 1MB limit
...options
};
const results = {
records: [],
errors: [],
stats: {
totalLines: 0,
validRecords: 0,
skippedRecords: 0
}
};
try {
const parser = parse(safeOptions);
parser.on('data', (record) => {
results.records.push(record);
results.stats.validRecords++;
});
parser.on('skip', (err, rawRecord) => {
results.errors.push({
code: err?.code,
message: err?.message,
line: err?.lines,
raw: rawRecord
});
results.stats.skippedRecords++;
});
parser.on('end', () => {
results.stats.totalLines = parser.info.lines;
});
parser.write(data);
parser.end();
return results;
} catch (err) {
throw new Error(`Fatal CSV parsing error: ${err.message}`);
}
}
// Usage
try {
const result = await safeCsvParse(csvData, { columns: true });
console.log(`Parsed ${result.stats.validRecords} records`);
if (result.errors.length > 0) {
console.warn(`${result.errors.length} records had errors`);
}
} catch (err) {
console.error("Failed to parse CSV:", err.message);
}Tools and techniques for debugging CSV parsing issues.
Usage Examples:
import { parse, CsvError } from "csv-parse";
// Enhanced error reporting
function debugCsvParsing(data, options = {}) {
const debugOptions = {
...options,
info: true, // Include parsing info
raw: true, // Include raw data
skip_records_with_error: true
};
const parser = parse(debugOptions);
const diagnostics = {
errors: [],
warnings: [],
records: []
};
parser.on('data', (record) => {
diagnostics.records.push({
line: record.info.lines,
data: record.record,
raw: record.raw
});
});
parser.on('skip', (err, rawRecord) => {
diagnostics.errors.push({
line: err?.lines || 'unknown',
code: err?.code,
message: err?.message,
raw: rawRecord,
context: {
column: err?.column,
quoting: err?.quoting,
escape: err?.escape
}
});
});
parser.on('end', () => {
const info = parser.info;
console.log('Parsing Diagnostics:', {
totalLines: info.lines,
totalRecords: info.records,
commentLines: info.comment_lines,
emptyLines: info.empty_lines,
bytesProcessed: info.bytes,
invalidFieldLength: info.invalid_field_length,
errors: diagnostics.errors.length,
successRate: `${((info.records / info.lines) * 100).toFixed(2)}%`
});
if (diagnostics.errors.length > 0) {
console.log('Error Details:');
diagnostics.errors.forEach((error, index) => {
console.log(` ${index + 1}. Line ${error.line}: ${error.code} - ${error.message}`);
console.log(` Raw: ${error.raw}`);
});
}
});
parser.write(data);
parser.end();
return diagnostics;
}
// Error pattern analysis
function analyzeCsvErrors(csvData, options) {
const errorPatterns = new Map();
parse(csvData, {
...options,
skip_records_with_error: true,
on_skip: (err, rawRecord) => {
const pattern = err?.code || 'UNKNOWN';
if (!errorPatterns.has(pattern)) {
errorPatterns.set(pattern, {
count: 0,
examples: []
});
}
const patternData = errorPatterns.get(pattern);
patternData.count++;
if (patternData.examples.length < 3) {
patternData.examples.push({
line: err?.lines,
raw: rawRecord,
message: err?.message
});
}
}
});
console.log('Error Pattern Analysis:');
errorPatterns.forEach((data, pattern) => {
console.log(` ${pattern}: ${data.count} occurrences`);
data.examples.forEach((example, i) => {
console.log(` Example ${i + 1}: Line ${example.line} - ${example.raw}`);
});
});
}