# Error handling

## Exception hierarchy

```
RBridgeError              base class for all r_bridge exceptions
├── RStartupError         R process failed to start or timed out during startup
├── RTimeoutError         a call did not complete within the allowed timeout
├── RError                an error was raised on the R side
└── RProtocolError        R sent a malformed response envelope
```

All exceptions are importable from `r_bridge.exceptions` or directly from `r_bridge`.

## R-side errors

When R calls `stop()` or an unhandled error occurs, r_bridge raises `RError`.

```python
from r_bridge import RBridge
from r_bridge.exceptions import RError

with RBridge() as r:
    try:
        r.eval("stop('something went wrong')")
    except RError as e:
        print(e.message)    # "something went wrong"
        print(e.call)       # R call that triggered the error (or None)
        print(e.traceback)  # list of traceback strings from R
        print(e.warnings)   # list of warnings captured before the error
        print(e.stderr)     # accumulated R stderr at the time of the error
```

The bridge **stays alive** after an `RError`. You can continue making calls.

## Timeouts

```python
from r_bridge.exceptions import RTimeoutError

# Bridge-wide timeout (applies to all calls)
with RBridge(call_timeout=10.0) as r:
    ...

# Per-call timeout override
with RBridge() as r:
    try:
        r.eval("Sys.sleep(99)", timeout=1.0)
    except RTimeoutError:
        print("R took too long")
```

!!! warning
    After a `RTimeoutError` the bridge is marked dead and cannot be reused.
    Create a new `RBridge` instance to recover.

## Startup errors

```python
from r_bridge.exceptions import RStartupError

try:
    with RBridge(r_executable="/wrong/path/Rscript") as r:
        ...
except RStartupError as e:
    print(e)   # includes R stderr output to help diagnose the problem
```

## R warnings

R warnings are captured and **muffled** — they never appear as unhandled output.
After a successful call they are returned alongside the result and can be
inspected from the exception on error:

```python
import math

with RBridge() as r:
    val = r.log(-1.0)      # produces NaN + a warning in R
    print(math.isnan(val)) # True — the call succeeded despite the warning
```

## Verbose mode

`verbose=True` is the easiest way to see what is happening in real time:

```python
with RBridge(verbose=True) as r:
    r.x = [1, 2, 3]
    r.mean(r.x)
```

All output goes to `sys.stderr`:

```
[R set]    x = {'__type__': 'integer_vector', 'value': [1, 2, 3]}
[R call]   mean([...])
[R stdout] ...    ← from cat() in R
[R stderr] ...    ← from message() / warnings in R
```

| Prefix | Source |
|---|---|
| `[R call]` | `call_func` operation |
| `[R eval]` | `eval_expr` operation |
| `[R exec]` | `exec_script` operation (first line of script shown) |
| `[R set]`  | `set_var` operation |
| `[R get]`  | `get_var` operation |
| `[R stdout]` | non-protocol R stdout (e.g. `cat()`) |
| `[R stderr]` | R stderr (e.g. `message()`, warnings) |
