Skip to content

Commit 3adc9f0

Browse files
feat: add structured error handling with class error hierarchy
1 parent 3ad842c commit 3adc9f0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3061
-271
lines changed

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- JSON safe serialization improvements
4343
- Promise-based service calls implementation
4444
- Add ParameterClient for external parameter access
45+
- Add structured error handling with class error hierarchy
4546

4647
- **[Martins Mozeiko](https://github.com/martins-mozeiko)**
4748
- QoS new/delete fix

example/error-handling/README.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# Error Handling Examples
2+
3+
This directory contains examples demonstrating structured error handling in rclnodejs. These examples showcase the comprehensive error class hierarchy for better debugging and error recovery patterns.
4+
5+
## Overview
6+
7+
rclnodejs provides 21 specialized error classes organized in a clear hierarchy, making it easier to catch, handle, and debug errors with rich context. All errors extend from `RclNodeError` and include properties like `code`, `nodeName`, `entityType`, `entityName`, and detailed context information.
8+
9+
## Error Hierarchy
10+
11+
```
12+
RclNodeError (base)
13+
├── ValidationError
14+
│ ├── TypeValidationError
15+
│ ├── RangeValidationError
16+
│ └── NameValidationError
17+
├── OperationError
18+
│ ├── TimeoutError
19+
│ ├── AbortError
20+
│ ├── ServiceNotFoundError
21+
│ └── NodeNotFoundError
22+
├── ParameterError
23+
│ ├── ParameterNotFoundError
24+
│ ├── ParameterTypeError
25+
│ └── ReadOnlyParameterError
26+
├── TopicError
27+
│ ├── PublisherError
28+
│ └── SubscriptionError
29+
├── ActionError
30+
│ ├── GoalRejectedError
31+
│ └── ActionServerNotFoundError
32+
└── NativeError
33+
```
34+
35+
## Examples
36+
37+
### 1. Type Validation (`error-handling-example.js` - Example 1)
38+
39+
**Purpose**: Demonstrates catching `TypeValidationError` when providing wrong argument types.
40+
41+
- **Functionality**: Attempts to create node with invalid type, catches and displays error details
42+
- **Features**: Shows `argumentName`, `expectedType`, and `providedValue` properties
43+
- **Key Properties**: `error.argumentName`, `error.expectedType`, `error.providedValue`
44+
45+
### 2. Range Validation (`error-handling-example.js` - Example 2)
46+
47+
**Purpose**: Demonstrates catching `RangeValidationError` for out-of-bounds values.
48+
49+
- **Functionality**: Attempts to create rate with invalid frequency (>1000 Hz), catches error
50+
- **Features**: Shows `validationRule` and `providedValue` for constraint violations
51+
- **Key Properties**: `error.validationRule`, `error.providedValue`, `error.nodeName`
52+
53+
### 3. Service Errors (`error-handling-example.js` - Example 3)
54+
55+
**Purpose**: Demonstrates `TimeoutError` and `AbortError` handling for service operations.
56+
57+
- **Functionality**: Shows timeout handling and manual request cancellation
58+
- **Features**: Demonstrates timeout with `sendRequestAsync({ timeout })` and `AbortController`
59+
- **Key Properties**: `error.timeout`, `error.operationType`, `error.entityName`
60+
61+
### 4. Publisher Errors (`error-handling-example.js` - Example 4)
62+
63+
**Purpose**: Demonstrates catching `PublisherError` during publisher creation.
64+
65+
- **Functionality**: Attempts to create publisher with invalid message type
66+
- **Features**: Shows error handling for topic creation failures
67+
- **Key Class**: `PublisherError`
68+
69+
### 5. Subscription Errors (`error-handling-example.js` - Example 5)
70+
71+
**Purpose**: Demonstrates catching `SubscriptionError` during subscription creation.
72+
73+
- **Functionality**: Attempts to create subscription with invalid message type
74+
- **Features**: Shows error handling for subscription creation failures
75+
- **Key Class**: `SubscriptionError`
76+
77+
### 6. Parameter Errors (`error-handling-example.js` - Example 6)
78+
79+
**Purpose**: Demonstrates `ParameterTypeError` for parameter type mismatches.
80+
81+
- **Functionality**: Creates parameter with wrong value type, catches type error
82+
- **Features**: Shows parameter validation and type checking
83+
- **Key Class**: `ParameterTypeError`
84+
85+
### 7. Name Validation (`error-handling-example.js` - Example 7)
86+
87+
**Purpose**: Demonstrates `NameValidationError` for invalid ROS names.
88+
89+
- **Functionality**: Validates topic name with invalid characters
90+
- **Features**: Shows `invalidIndex` property pointing to error location
91+
- **Key Properties**: `error.invalidIndex`, `error.message`
92+
93+
### 8. Error Recovery (`error-handling-example.js` - Example 8)
94+
95+
**Purpose**: Demonstrates retry logic with structured error handling.
96+
97+
- **Functionality**: Implements retry pattern with different handling for timeout vs abort
98+
- **Features**: Shows differentiation between recoverable and non-recoverable errors
99+
- **Pattern**: Retry on `TimeoutError`, abort on `AbortError` or other errors
100+
101+
### 9. Error Serialization (`error-handling-example.js` - Example 9)
102+
103+
**Purpose**: Demonstrates using `toJSON()` for logging and debugging.
104+
105+
- **Functionality**: Serializes error to JSON for structured logging
106+
- **Features**: Shows `toJSON()` method producing complete error information
107+
- **Method**: `error.toJSON()`
108+
109+
### 10. Generic Error Handler (`error-handling-example.js` - Example 10)
110+
111+
**Purpose**: Demonstrates reusable error handler for all rclnodejs errors.
112+
113+
- **Functionality**: Implements `handleRclError()` function for consistent error handling
114+
- **Features**: Extracts common error properties, handles specific error types
115+
- **Pattern**: Single function handling all `RclNodeError` subclasses
116+
117+
## How to Run
118+
119+
1. **Prerequisites**: Ensure ROS 2 is installed and sourced
120+
121+
2. **Run All Examples**:
122+
123+
```bash
124+
node example/error-handling/error-handling-example.js
125+
```
126+
127+
3. **Expected Output**: Demonstrates all 10 error handling patterns with clear success indicators
128+
129+
## Key Concepts Demonstrated
130+
131+
### Error Properties
132+
133+
All `RclNodeError` instances include:
134+
135+
- `name`: Error class name (e.g., `'TypeValidationError'`)
136+
- `message`: Human-readable error description
137+
- `code`: Machine-readable error code (e.g., `'INVALID_TYPE'`)
138+
- `nodeName`: Associated node name (when applicable)
139+
- `entityType`: Entity type causing error (e.g., `'service'`, `'publisher'`)
140+
- `entityName`: Specific entity name (e.g., topic/service name)
141+
- `timestamp`: Error creation timestamp
142+
143+
### Type-Specific Properties
144+
145+
- **TypeValidationError**: `argumentName`, `expectedType`, `providedValue`
146+
- **RangeValidationError**: `validationRule`, `providedValue`
147+
- **NameValidationError**: `invalidIndex`
148+
- **TimeoutError**: `timeout`, `operationType`
149+
- **ParameterTypeError**: `argumentName`, `expectedType`
150+
151+
### Error Methods
152+
153+
- `toJSON()`: Serialize error to plain object for logging
154+
- `toString()`: Format error as readable string with context
155+
156+
### Error Handling Patterns
157+
158+
- **Type Guards**: Use `instanceof` to check specific error types
159+
- **Recovery Logic**: Different handling for recoverable vs non-recoverable errors
160+
- **Error Chaining**: Support for `cause` property linking related errors
161+
- **Structured Logging**: Use `toJSON()` for logging systems
162+
- **Generic Handlers**: Single function handling multiple error types
163+
164+
## Usage Examples
165+
166+
### Basic Error Catching
167+
168+
```javascript
169+
try {
170+
rclnodejs.createNode(123, 'namespace');
171+
} catch (error) {
172+
if (error instanceof rclnodejs.TypeValidationError) {
173+
console.log(
174+
`Expected ${error.expectedType}, got ${typeof error.providedValue}`
175+
);
176+
}
177+
}
178+
```
179+
180+
### Service Timeout Handling
181+
182+
```javascript
183+
try {
184+
await client.sendRequestAsync(request, { timeout: 5000 });
185+
} catch (error) {
186+
if (error instanceof rclnodejs.TimeoutError) {
187+
console.log(`Timeout after ${error.timeout}ms`);
188+
} else if (error instanceof rclnodejs.AbortError) {
189+
console.log('Request cancelled');
190+
}
191+
}
192+
```
193+
194+
### Generic Error Handler
195+
196+
```javascript
197+
function handleRclError(error, operation) {
198+
if (!(error instanceof rclnodejs.RclNodeError)) {
199+
console.error(`Unexpected error: ${error.message}`);
200+
return;
201+
}
202+
203+
const context = [];
204+
if (error.nodeName) context.push(`node: ${error.nodeName}`);
205+
if (error.entityName)
206+
context.push(`${error.entityType}: ${error.entityName}`);
207+
208+
console.error(`${error.name} in ${operation}: ${error.message}`);
209+
}
210+
```
211+
212+
## Notes
213+
214+
- All error classes extend `RclNodeError` for consistent `instanceof` checks
215+
- Error context properties are populated based on the operation
216+
- TypeScript definitions provide full type safety for error handling
217+
- Use `toJSON()` for integration with logging frameworks
218+
- Error codes are machine-readable constants for programmatic handling
219+
- Backward compatibility maintained - existing error handling continues to work

0 commit comments

Comments
 (0)