meld
Version:
Meld: A template language for LLM prompts
283 lines (211 loc) • 9.47 kB
Markdown
# Error Handling in Meld
This document describes the error handling architecture in Meld, including how errors are classified, how they're handled in different modes, and how to work with the error system as a developer.
## Table of Contents
1. [Overview](#overview)
2. [Error Severity Levels](#error-severity-levels)
3. [Strict vs. Permissive Mode](#strict-vs-permissive-mode)
4. [Error Classification Guidelines](#error-classification-guidelines)
5. [Working with Errors](#working-with-errors)
- [Creating Errors](#creating-errors)
- [Handling Errors](#handling-errors)
- [Testing Errors](#testing-errors)
6. [Error Types Reference](#error-types-reference)
7. [Source Mapping](#source-mapping)
8. [Best Practices](#best-practices)
## Overview
Meld's error handling system is designed to support two different modes of operation:
1. **Strict Mode**: All errors are thrown, providing immediate feedback during development and testing.
2. **Permissive Mode**: Recoverable errors are converted to warnings, allowing processing to continue when possible.
This dual-mode approach allows Meld to be both rigorous during development and forgiving during end-user usage, particularly in the CLI.
## Error Severity Levels
All errors in Meld are classified with one of three severity levels:
- **Fatal**: Errors that always halt execution, regardless of mode.
- **Recoverable**: Errors that throw in strict mode but become warnings in permissive mode.
- **Warning**: Issues that never throw and are always treated as warnings.
These severity levels are defined in the `ErrorSeverity` enum:
```typescript
export enum ErrorSeverity {
Fatal = 'fatal',
Recoverable = 'recoverable',
Warning = 'warning'
}
```
## Strict vs. Permissive Mode
### Strict Mode
Strict mode is the default for most services and is used during development and testing. In strict mode:
- All errors (Fatal and Recoverable) are thrown
- Warnings are logged but don't interrupt execution
- Provides immediate feedback about issues
Example of enabling strict mode:
```typescript
const result = await interpreterService.interpret(nodes, {
strict: true,
// other options...
});
```
### Permissive Mode
Permissive mode is used by the CLI and other user-facing interfaces. In permissive mode:
- Only Fatal errors are thrown
- Recoverable errors are converted to warnings
- Warnings are logged but don't interrupt execution
- Processing continues when possible
Example of enabling permissive mode with a custom error handler:
```typescript
const warnings: MeldError[] = [];
const errorHandler = (error: MeldError) => {
warnings.push(error);
console.warn(`Warning: ${error.message}`);
};
const result = await interpreterService.interpret(nodes, {
strict: false,
errorHandler,
// other options...
});
```
## Error Classification Guidelines
Errors in Meld are classified according to these guidelines:
### Fatal Errors (Always Throw)
- Syntax errors (MeldParseError)
- Circular imports (DirectiveError with CIRCULAR_REFERENCE)
- Invalid directive types (DirectiveError with HANDLER_NOT_FOUND)
- Missing required fields in directives (DirectiveError with VALIDATION_FAILED)
- Type validation failures (PathValidationError with INVALID_PATH)
- File system access errors (MeldFileSystemError)
- Service initialization errors (ServiceInitializationError)
### Recoverable Errors (Warnings in Permissive Mode)
- Missing data fields (MeldResolutionError)
- Undefined variables (MeldResolutionError)
- Missing environment variables (MeldResolutionError)
- File not found for embed/import (MeldFileNotFoundError)
- Invalid field access (MeldResolutionError)
- Command execution failures (MeldInterpreterError)
### Always Warnings (Never Throw)
- Deprecated features
- Performance suggestions
- Non-critical validation issues
## Working with Errors
### Creating Errors
When creating errors, always specify the appropriate severity level:
```typescript
// Creating a fatal error
throw new MeldError('Critical failure', {
severity: ErrorSeverity.Fatal,
code: 'CRITICAL_ERROR',
context: { /* additional context */ }
});
// Creating a recoverable error
throw new MeldResolutionError('Variable not found', {
severity: ErrorSeverity.Recoverable,
details: {
variableName: 'myVar',
variableType: 'text'
}
});
// Creating a warning
const warning = new MeldError('Performance suggestion', {
severity: ErrorSeverity.Warning,
code: 'PERF_SUGGESTION'
});
logger.warn(warning.message, warning);
```
### Handling Errors
Services that need to handle errors should respect the strict/permissive mode:
```typescript
try {
// Attempt operation
} catch (error) {
if (error instanceof MeldError) {
// Check if we're in permissive mode and the error is recoverable
if (!options.strict && error.canBeWarning()) {
// Handle as warning
if (options.errorHandler) {
options.errorHandler(error);
} else {
logger.warn(`Warning: ${error.message}`, error);
}
// Continue processing
return fallbackValue;
}
}
// Re-throw fatal errors or in strict mode
throw error;
}
```
### Testing Errors
Meld provides utilities for testing error handling in both strict and permissive modes:
```typescript
import {
ErrorCollector,
expectErrorSeverity,
expectThrowsWithSeverity,
expectWarningsInPermissiveMode,
expectThrowsInStrictButWarnsInPermissive
} from '@tests/utils';
// Test that a function throws with the correct severity
await expectThrowsWithSeverity(
() => resolver.resolve('${undefined}', context),
MeldResolutionError,
ErrorSeverity.Recoverable
);
// Test behavior in both modes
await expectThrowsInStrictButWarnsInPermissive(
(options) => resolver.resolve('${undefined}', context, options),
MeldResolutionError
);
```
## Error Types Reference
Meld has several specialized error types:
- **MeldError**: Base class for all Meld errors
- **MeldParseError**: Errors during parsing
- **MeldDirectiveError**: Base class for directive-related errors
- **DirectiveError**: Specific directive processing errors
- **MeldResolutionError**: Variable resolution errors
- **MeldInterpreterError**: Errors during interpretation
- **MeldFileSystemError**: File system access errors
- **MeldFileNotFoundError**: File not found errors
- **MeldImportError**: Import-related errors
- **MeldOutputError**: Output generation errors
- **PathValidationError**: Path validation errors
Each error type includes:
- A message describing the error
- A severity level
- Optional context information
- Optional error code
- Optional file path
## Source Mapping
Meld includes source mapping to provide better error reporting by tracking the original file locations when content is imported or embedded from other files. This helps users identify the actual source of errors instead of seeing errors in the combined or transformed content.
### How Source Mapping Works
1. **Registration**: When files are imported or embedded, the content is registered with the source mapping system.
2. **Mapping Creation**: Line mappings are created to track where content from source files appears in the combined output.
3. **Error Enhancement**: When errors occur, they're automatically enhanced with information about the original source location.
4. **User-Friendly Messages**: Error messages include both the combined file location and the original source file/line.
### Example
Instead of seeing:
```
Error: Invalid syntax at line 42 in main.meld
```
Users will see:
```
Error in /path/to/imported-file.meld:5: Invalid syntax at line 42 in main.meld
```
This makes debugging much easier, especially for files that import or embed content from multiple sources.
### Source Mapping Integration
Source mapping is integrated into the following components:
- **EmbedDirectiveHandler**: Maps embedded content to its original source file
- **ImportDirectiveHandler**: Maps imported content to its original source file
- **ParserService**: Enhances parser errors with source file information
- **OutputService**: Enhances LLMXML errors with source file information
### Implementation Details
Source mappings are maintained via a singleton `SourceMapService` that tracks source files and creates mappings between source locations and combined file locations. The service is accessed through utility functions that register sources, add mappings, and enhance errors with source information.
## Best Practices
1. **Always specify severity**: When creating errors, always specify the appropriate severity level.
2. **Add context**: Include relevant context in errors to help with debugging.
3. **Use specific error types**: Use the most specific error type for the situation.
4. **Respect strict/permissive mode**: Services should respect the strict/permissive mode when handling errors.
5. **Test both modes**: Write tests for both strict and permissive modes.
6. **Use error codes**: Use consistent error codes to help with error identification.
7. **Document error codes**: Document error codes and their meanings.
8. **Provide helpful error messages**: Error messages should be clear and helpful.
9. **Include recovery suggestions**: When possible, include suggestions for how to recover from errors.
10. **Log warnings appropriately**: Use the appropriate logging level for warnings.
11. **Preserve source info**: When wrapping errors, preserve source file information from the original error.