From 01ba8143e5c9d599e5ba576b97ca9eb6e01bc605 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Sun, 16 Nov 2025 22:52:52 -0500 Subject: [PATCH 1/2] Fix exception handling in Kotlin request executor Added comprehensive exception handling to prevent UnexpectedUniFFICallbackError crashes when network exceptions occur. The executor now properly catches and maps all exceptions to appropriate Rust error types. Without proper exception handling, uncaught exceptions from OkHttp would crash the UniFFI callback layer instead of being returned as proper errors to Rust. This was causing API discovery and other network operations to fail with UnexpectedUniFFICallbackError instead of meaningful error messages. Changes: - Add ConnectException handler to map connection failures to HttpError - Add catch-all Exception handler to map unexpected errors to GenericError - Add testLocalSite integration test to verify localhost API discovery works --- .../integrationTest/kotlin/ApiUrlDiscoveryTest.kt | 9 +++++++++ .../rs/wordpress/api/kotlin/WpRequestExecutor.kt | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/native/kotlin/api/kotlin/src/integrationTest/kotlin/ApiUrlDiscoveryTest.kt b/native/kotlin/api/kotlin/src/integrationTest/kotlin/ApiUrlDiscoveryTest.kt index 7dcce7e77..4492b3f41 100644 --- a/native/kotlin/api/kotlin/src/integrationTest/kotlin/ApiUrlDiscoveryTest.kt +++ b/native/kotlin/api/kotlin/src/integrationTest/kotlin/ApiUrlDiscoveryTest.kt @@ -27,6 +27,15 @@ import kotlin.test.assertContains class ApiUrlDiscoveryTest { private val loginClient: WpLoginClient = WpLoginClient() + @Test + fun testLocalSite() = runTest { + assertEquals( + "http://localhost/wp-admin/authorize-application.php", + loginClient.apiDiscovery("http://localhost") + .assertSuccess().applicationPasswordsAuthenticationUrl.url() + ) + } + @Test // Spec Example 1 fun testValidSiteWorksCorrectly() = runTest { assertEquals( diff --git a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt index 149ead33e..d17221a7a 100644 --- a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt +++ b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt @@ -25,6 +25,7 @@ import uniffi.wp_api.WpNetworkRequest import uniffi.wp_api.WpNetworkResponse import uniffi.wp_api.parseCertificate import java.io.File +import java.net.ConnectException import java.net.NoRouteToHostException import java.net.UnknownHostException import javax.net.ssl.HttpsURLConnection @@ -162,6 +163,18 @@ class WpRequestExecutor( throw requestExecutionFailedWith(RequestExecutionErrorReason.unknownHost(e)) } catch (e: NoRouteToHostException) { throw requestExecutionFailedWith(RequestExecutionErrorReason.noRouteToHost(e)) + } catch (e: ConnectException) { + throw requestExecutionFailedWith( + RequestExecutionErrorReason.HttpError( + reason = "Connection failed: ${e.localizedMessage}" + ) + ) + } catch (e: Exception) { + throw requestExecutionFailedWith( + RequestExecutionErrorReason.GenericError( + errorMessage = e.localizedMessage ?: e.toString() + ) + ) } } From 1d67b619b161c88104fb8854528bc813d0a85ce6 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 17 Nov 2025 03:34:54 -0500 Subject: [PATCH 2/2] Add detekt suppressions for intentional exception handling Suppress TooGenericExceptionCaught and SwallowedException warnings in the request executor. These are intentional design choices to ensure all network exceptions are properly converted to Rust error types instead of crashing the UniFFI callback layer. --- .../main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt index d17221a7a..eb7cb913d 100644 --- a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt +++ b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/api/kotlin/WpRequestExecutor.kt @@ -131,7 +131,9 @@ class WpRequestExecutor( ) } - @Suppress("ThrowsCount") + // We intentionally catch all exceptions to prevent UniFFI callback crashes. + // All exceptions are converted to proper Rust error types rather than being swallowed. + @Suppress("ThrowsCount", "TooGenericExceptionCaught", "SwallowedException") private fun executeRequestSafely( urlRequest: Request, requestUrl: String,