TONL Error Handling Guide

December 20, 2025 ยท View on GitHub

Version: 2.5.2 Status: Stable & Production Ready Last Updated: 2025-12-20

This guide covers error handling in TONL, including error types, best practices, and troubleshooting.


Table of Contents

  1. Error Classes
  2. Error Messages
  3. Catching Errors
  4. Security Considerations
  5. Troubleshooting
  6. Best Practices

Error Classes

TONL provides a hierarchy of error classes for different failure scenarios:

TONLError (Base Class)

Base class for all TONL-specific errors with location tracking.

import { TONLError } from 'tonl/errors';

class TONLError extends Error {
  line?: number;      // Line number where error occurred
  column?: number;    // Column number
  source?: string;    // Source code snippet
}

Example:

try {
  decodeTONL(invalidContent);
} catch (e) {
  if (e instanceof TONLError) {
    console.log(`Error at line ${e.line}: ${e.message}`);
  }
}

TONLParseError

Thrown for syntax errors during parsing.

import { TONLParseError } from 'tonl/errors';

class TONLParseError extends TONLError {
  suggestion?: string;  // Helpful suggestion for fixing
}

Common Causes:

  • Invalid header format
  • Unclosed quotes
  • Malformed lines
  • Invalid delimiters

Example:

try {
  TONLDocument.parse(`
    invalid: content: here
  `);
} catch (e) {
  if (e instanceof TONLParseError) {
    console.log(e.message);      // Error description
    console.log(e.suggestion);   // How to fix it
  }
}

TONLValidationError

Thrown when data fails schema validation.

import { TONLValidationError } from 'tonl/errors';

class TONLValidationError extends TONLError {
  field: string;      // Field that failed validation
  expected?: string;  // Expected value/type
  actual?: string;    // Actual value/type
}

Example:

import { validateTONL, parseSchema } from 'tonl/schema';

const schema = parseSchema(`@schema v1
age: u32 min:0 max:150
`);

const result = validateTONL({ age: -5 }, schema);
if (!result.valid) {
  for (const error of result.errors) {
    console.log(`Field: ${error.field}`);
    console.log(`Expected: ${error.expected}`);
    console.log(`Actual: ${error.actual}`);
  }
}

TONLTypeError

Thrown for type mismatch errors.

import { TONLTypeError } from 'tonl/errors';

class TONLTypeError extends TONLError {
  expected: string;  // Expected type
  actual: string;    // Actual type
}

Example:

try {
  doc.push('notAnArray', 'value');
} catch (e) {
  if (e instanceof TONLTypeError) {
    console.log(`Expected ${e.expected}, got ${e.actual}`);
  }
}

SecurityError

Thrown for security-related issues.

import { SecurityError } from 'tonl/errors';

class SecurityError extends Error {
  details?: Record<string, any>;  // Additional context
}

Triggers:

  • Prototype pollution attempts
  • Path traversal attacks
  • ReDoS (regex denial of service)
  • Buffer overflow attempts
  • Excessive nesting depth

Example:

try {
  // Attempting prototype pollution
  doc.set('__proto__.polluted', true);
} catch (e) {
  if (e instanceof SecurityError) {
    console.log('Security violation:', e.message);
    console.log('Details:', e.details);
  }
}

Error Messages

TONL uses centralized, consistent error messages:

Parse Errors

ErrorDescription
UNEXPECTED_TOKENUnexpected character during parsing
INVALID_HEADERMalformed header line
UNCLOSED_QUOTEString quote not closed
INVALID_DELIMITERUnsupported delimiter character
MALFORMED_LINELine doesn't match expected format

Type Errors

ErrorDescription
TYPE_MISMATCHValue type doesn't match expected
INVALID_INDEXArray index out of bounds
NOT_AN_ARRAYExpected array, got something else
NOT_AN_OBJECTExpected object, got something else

Security Errors

ErrorDescription
PROTOTYPE_POLLUTIONAttempt to access __proto__ or similar
PATH_TRAVERSALAttempt to escape allowed directories
REGEX_TOO_LONGRegex pattern exceeds safe length
REGEX_TOO_COMPLEXRegex nesting depth too high
DANGEROUS_REGEXKnown ReDoS pattern detected

Resource Limit Errors

ErrorDescription
INPUT_TOO_LARGEInput exceeds maximum size
LINE_TOO_LONGSingle line exceeds maximum length
DEPTH_EXCEEDEDNesting depth exceeds maximum
BLOCK_LINES_EXCEEDEDBlock has too many lines
BUFFER_OVERFLOWStream buffer would overflow

Query Errors

ErrorDescription
INVALID_PATHPath expression is invalid
FILTER_SYNTAXFilter expression has syntax error
QUERY_TOO_DEEPQuery nesting exceeds maximum

Schema Errors

ErrorDescription
SCHEMA_VIOLATIONData violates schema constraint
REQUIRED_FIELDRequired field is missing
INVALID_ENUMValue not in allowed enum list
PATTERN_MISMATCHValue doesn't match regex pattern

File Errors

ErrorDescription
FILE_NOT_FOUNDFile doesn't exist
FILE_LOCKEDFile is locked by another process
BACKUP_NOT_FOUNDBackup file for recovery not found

Catching Errors

Basic Error Handling

import { decodeTONL } from 'tonl';
import { TONLError, TONLParseError } from 'tonl/errors';

function safeParse(content: string) {
  try {
    return decodeTONL(content);
  } catch (e) {
    if (e instanceof TONLParseError) {
      console.error('Parse error:', e.message);
      if (e.suggestion) {
        console.log('Suggestion:', e.suggestion);
      }
      return null;
    }

    if (e instanceof TONLError) {
      console.error('TONL error:', e.message);
      return null;
    }

    // Re-throw unknown errors
    throw e;
  }
}

Async/Await with Errors

import { TONLDocument } from 'tonl';
import { SecurityError } from 'tonl/errors';

async function loadDocument(path: string) {
  try {
    return await TONLDocument.load(path);
  } catch (e) {
    if (e instanceof SecurityError) {
      console.error('Security issue:', e.message);
      // Log for security audit
      logSecurityEvent(e);
      return null;
    }
    throw e;
  }
}

Validation Result Handling

Schema validation returns a result object instead of throwing:

import { validateTONL, parseSchema } from 'tonl/schema';

const schema = parseSchema(`@schema v1
name: str required min:2
age: u32 min:0 max:150
email: str pattern:email
`);

const data = {
  name: 'A',        // Too short
  age: 200,         // Out of range
  // email missing
};

const result = validateTONL(data, schema);

if (!result.valid) {
  console.log(`Found ${result.errors.length} validation errors:`);

  for (const error of result.errors) {
    console.log(`- ${error.field}: ${error.message}`);
  }
}

// Output:
// Found 3 validation errors:
// - name: Value too short (min: 2)
// - age: Value exceeds maximum (max: 150)
// - email: Required field is missing

Security Considerations

Development vs Production Mode

Error detail level is controlled by NODE_ENV:

// Development mode (NODE_ENV=development)
// Full error details including source code

// Production mode (NODE_ENV=production or unset)
// Minimal error info to prevent information leakage

Development output:

TONLParseError: Unexpected token '{'
  at line 5:12

    4 | name: Alice
  > 5 | invalid: {{{
           ^
    6 | age: 30

๐Ÿ’ก Suggestion: Check for unclosed braces

Production output:

TONLParseError: Unexpected token '{' (line 5)

Protected Properties

TONL prevents access to dangerous object properties:

const BLOCKED_PROPERTIES = [
  '__proto__',
  'constructor',
  'prototype',
  '__defineGetter__',
  '__defineSetter__',
  '__lookupGetter__',
  '__lookupSetter__'
];

Attempting to access these throws SecurityError:

try {
  doc.set('__proto__.polluted', true);
} catch (e) {
  // SecurityError: Access to '__proto__' is forbidden
}

Resource Limits

Default security limits:

ResourceLimit
Max input size10MB
Max line length100,000 chars
Max nesting depth100 levels
Max block lines10,000 lines
Max regex length1,000 chars
Max regex nesting5 levels
Max buffer size50MB

Troubleshooting

"Unexpected token" errors

Cause: Usually malformed TONL syntax.

Solution:

  1. Check for unclosed quotes or braces
  2. Verify delimiter consistency
  3. Check indentation (spaces vs tabs)
// Bad
name: "unclosed quote
items: [1, 2, 3

// Good
name: "closed quote"
items: [1, 2, 3]

"Maximum nesting depth exceeded"

Cause: Data structure too deeply nested.

Solution:

  1. Flatten data structure if possible
  2. Increase depth limit (not recommended for untrusted input)
// Increase limit (for trusted data only)
const doc = TONLDocument.parse(content, {
  maxDepth: 200
});

"Buffer overflow prevented"

Cause: Streaming input exceeds buffer size.

Solution:

  1. Process smaller chunks
  2. Use streaming with backpressure
const stream = createDecodeStream({
  highWaterMark: 64 * 1024  // 64KB chunks
});

"Path traversal detected"

Cause: File path attempts to escape allowed directory.

Solution:

  1. Validate and sanitize file paths
  2. Use absolute paths within allowed directories
import { resolve, relative } from 'path';

function safePath(userPath: string, baseDir: string): string {
  const resolved = resolve(baseDir, userPath);
  const rel = relative(baseDir, resolved);

  if (rel.startsWith('..')) {
    throw new Error('Path traversal attempt');
  }

  return resolved;
}

"Required field is missing"

Cause: Data doesn't include a field marked as required in schema.

Solution:

  1. Add the missing field
  2. Mark field as optional in schema
// Schema
@schema v1
name: str required
nickname: str  // Optional (no 'required')

// Data must include 'name'
{ name: "Alice" }  // โœ“ Valid
{ nickname: "Ali" }  // โœ— Missing 'name'

Best Practices

1. Always Catch Specific Error Types

try {
  // TONL operations
} catch (e) {
  if (e instanceof TONLParseError) {
    // Handle parse errors
  } else if (e instanceof TONLValidationError) {
    // Handle validation errors
  } else if (e instanceof SecurityError) {
    // Handle security issues
  } else {
    // Unknown error - re-throw or log
    throw e;
  }
}

2. Use Validation Results for User Input

// Don't throw on user input - use validation
const result = validateTONL(userInput, schema);
if (!result.valid) {
  return {
    success: false,
    errors: result.errors.map(e => ({
      field: e.field,
      message: e.message
    }))
  };
}

3. Log Security Errors

import { SecurityError } from 'tonl/errors';

try {
  processInput(untrustedData);
} catch (e) {
  if (e instanceof SecurityError) {
    // Log for security monitoring
    securityLogger.warn({
      type: 'TONL_SECURITY_ERROR',
      message: e.message,
      details: e.details,
      timestamp: new Date().toISOString()
    });

    // Return generic error to user
    throw new Error('Invalid input');
  }
  throw e;
}

4. Set Appropriate Limits for Untrusted Input

// For user-provided content
const strictOptions = {
  maxDepth: 20,
  maxLineLength: 10000,
  strict: true
};

const doc = TONLDocument.parse(userContent, strictOptions);

5. Use formatErrorLocation for Debugging

import { formatErrorLocation, TONLParseError } from 'tonl/errors';

try {
  decodeTONL(content);
} catch (e) {
  if (e instanceof TONLParseError && e.line !== undefined) {
    const lines = content.split('\n');
    const context = formatErrorLocation(lines, e.line - 1, e.column);
    console.log('Error context:');
    console.log(context);
  }
}

API Reference

Importing Error Classes

// All error classes
import {
  TONLError,
  TONLParseError,
  TONLValidationError,
  TONLTypeError,
  SecurityError,
  formatErrorLocation
} from 'tonl/errors';

// Error messages
import { ErrorMessages, type ErrorMessageKey } from 'tonl/errors';

Error Message Usage

import { ErrorMessages } from 'tonl/errors';

// Generate consistent error messages
const msg1 = ErrorMessages.TYPE_MISMATCH('string', 'number', 'user.age');
// "Expected string but got number at path 'user.age'"

const msg2 = ErrorMessages.REQUIRED_FIELD('email');
// "Required field 'email' is missing"

const msg3 = ErrorMessages.DEPTH_EXCEEDED(101, 100);
// "Maximum nesting depth exceeded: 101 (max: 100)"