# Error Handling PlestyLib centralizes most runtime error behavior through `handle_error` in `plesty.lib.utils.error_utils`. This utility standardizes logging and exception raising across device, traffic, and solver layers. ## Core Utility Signature: ```python handle_error(err, error_msg=None, silent=False, func_name="Unknown Function") ``` Parameters: 1. `err`: expected to be an Exception instance. 2. `error_msg`: optional high-level message to expose to callers. 3. `silent`: when `True`, only logs a warning and does not raise. 4. `func_name`: currently unused in implementation. ## Behavior Matrix ### Case 1: `silent=True` 1. Logs warning with `error_msg`. 2. Returns without raising. ### Case 2: `silent=False`, `error_msg is None`, `err` is Exception 1. Logs the original exception type and message. 2. Re-raises the same exception object. ### Case 3: `silent=False`, `error_msg is None`, `err` is not Exception 1. Logs invalid usage. 2. Raises `RuntimeError`. ### Case 4: `silent=False`, `error_msg` provided, `err` is Exception 1. Logs wrapped context plus cause. 2. Raises `RuntimeError(error_msg)` with chained cause (`raise ... from err`). ### Case 5: `silent=False`, `error_msg` provided, `err` is not Exception 1. Logs invalid usage. 2. Raises `RuntimeError` with combined message. ## Why This Pattern Is Used This project uses `handle_error` to: 1. Keep error messages consistent across modules. 2. Preserve root cause while exposing cleaner user-facing messages. 3. Avoid silent failures in most code paths. You can see usage in: 1. Traffic managers (`plesty/lib/traffic`). 2. Base device classes (`plesty/lib/device/base_*`). 3. Solvers (`plesty/lib/solver`). 4. Parameter system (`plesty/lib/device/params.py`). ## Usage Examples ### Re-raise the original exception ```python from plesty.lib.utils.error_utils import handle_error try: risky_call() except Exception as e: handle_error(e) ``` ### Raise a contextual runtime error with chained cause ```python from plesty.lib.utils.error_utils import handle_error try: device.connect() except Exception as e: handle_error(e, "Failed to connect to instrument") ``` ### Warning-only mode (rare) ```python from plesty.lib.utils.error_utils import handle_error try: optional_background_task() except Exception as e: handle_error(e, "Background task failed", silent=True) ``` ## Recommended Practices 1. Pass real Exception instances as `err`. 2. Provide `error_msg` when you want a stable caller-facing message. 3. Use warning-only mode (`silent=True`) only for explicitly non-critical flows. 4. Prefer fail-fast behavior in device I/O, protocol parsing, and parameter validation. 5. Preserve context by raising with chained causes when wrapping low-level errors. ## Common Pitfalls 1. Passing strings instead of Exception objects as `err`. 2. Using `silent=True` too broadly and hiding critical failures. 3. Expecting `func_name` to appear in output (it is currently unused). 4. Assuming wrapped errors keep original exception type when `error_msg` is provided. ## Integration Guidance Use this pattern by layer: 1. Traffic manager: wrap connection/read/write failures with transport-specific context. 2. Solver: wrap parse/serialization failures with operation context. 3. Device class: expose operation-level context such as command key and device identity. 4. Param/func system: raise validation errors early, before sending traffic.