Wrap raw Netty / I/O exceptions in a CCXT NetworkError so the test harness's isTemporaryFailure() check (instanceof OperationFailed) treats them as transient — matches Python's WS client behaviour, which raises `NetworkError(str(exception))` from connection failures (handshake 4xx, SSL errors, timeo
(Object error)
| 1804 | * Already-CCXT errors (BaseError descendants) and plain strings pass through. |
| 1805 | */ |
| 1806 | private Object wrapAsNetworkError(Object error) { |
| 1807 | if (error instanceof io.github.ccxt.errors.BaseError) { |
| 1808 | return error; |
| 1809 | } |
| 1810 | if (error instanceof Throwable t) { |
| 1811 | String pkg = t.getClass().getName(); |
| 1812 | boolean isNetwork = |
| 1813 | pkg.startsWith("io.netty.") |
| 1814 | || t instanceof java.net.SocketException |
| 1815 | || t instanceof java.net.UnknownHostException |
| 1816 | || t instanceof java.io.IOException |
| 1817 | || t instanceof java.util.concurrent.TimeoutException |
| 1818 | || t instanceof javax.net.ssl.SSLException; |
| 1819 | if (isNetwork) { |
| 1820 | String msg = t.getMessage() != null ? t.getMessage() : t.getClass().getSimpleName(); |
| 1821 | io.github.ccxt.errors.NetworkError wrapped = |
| 1822 | new io.github.ccxt.errors.NetworkError(msg); |
| 1823 | wrapped.initCause(t); |
| 1824 | return wrapped; |
| 1825 | } |
| 1826 | } |
| 1827 | return error; |
| 1828 | } |
| 1829 | |
| 1830 | /** |
| 1831 | * Fire-and-forget async task (matches JS spawn() / C# Task.Run()). |