From df1452799b28782ebfe273b4dcaf779cf34ced38 Mon Sep 17 00:00:00 2001 From: "Ben.Li" Date: Sat, 4 Jul 2026 02:24:50 +0800 Subject: [PATCH] fix(android): preserve numeric invoke error codes (#99591) * fix(android): preserve numeric invoke error codes * fix(android): tighten invoke error code parsing --------- Co-authored-by: Peter Steinberger --- .../openclaw/app/gateway/InvokeErrorParser.kt | 4 +++- .../app/gateway/InvokeErrorParserTest.kt | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/android/app/src/main/java/ai/openclaw/app/gateway/InvokeErrorParser.kt b/apps/android/app/src/main/java/ai/openclaw/app/gateway/InvokeErrorParser.kt index 5cda3235fb57..fb4fad2e17e9 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/gateway/InvokeErrorParser.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/gateway/InvokeErrorParser.kt @@ -1,5 +1,7 @@ package ai.openclaw.app.gateway +private val invokeErrorCodePattern = Regex("^[A-Z][A-Z0-9_]*$") + data class ParsedInvokeError( val code: String, val message: String, @@ -24,7 +26,7 @@ fun parseInvokeErrorMessage(raw: String): ParsedInvokeError { if (parts.size == 2) { val code = parts[0].trim() val rest = parts[1].trim() - if (code.isNotEmpty() && code.all { it.isUpperCase() || it == '_' }) { + if (invokeErrorCodePattern.matches(code)) { return ParsedInvokeError( code = code, message = rest.ifEmpty { trimmed }, diff --git a/apps/android/app/src/test/java/ai/openclaw/app/gateway/InvokeErrorParserTest.kt b/apps/android/app/src/test/java/ai/openclaw/app/gateway/InvokeErrorParserTest.kt index f30cd27ed5ca..3740da8cdb0b 100644 --- a/apps/android/app/src/test/java/ai/openclaw/app/gateway/InvokeErrorParserTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/app/gateway/InvokeErrorParserTest.kt @@ -15,12 +15,28 @@ class InvokeErrorParserTest { assertEquals("CAMERA_PERMISSION_REQUIRED: grant Camera permission", parsed.prefixedMessage) } + @Test + fun parseInvokeErrorMessage_parsesNumericCodePrefix() { + val parsed = parseInvokeErrorMessage("A2UI_HOST_UNAVAILABLE: bundled A2UI host not reachable") + assertEquals("A2UI_HOST_UNAVAILABLE", parsed.code) + assertEquals("bundled A2UI host not reachable", parsed.message) + assertTrue(parsed.hadExplicitCode) + } + @Test fun parseInvokeErrorMessage_rejectsNonCanonicalCodePrefix() { - val parsed = parseInvokeErrorMessage("IllegalStateException: boom") - assertEquals("UNAVAILABLE", parsed.code) - assertEquals("IllegalStateException: boom", parsed.message) - assertFalse(parsed.hadExplicitCode) + listOf( + "IllegalStateException: boom", + "2FAST: boom", + "_PRIVATE: boom", + "CAMERA-PERMISSION: boom", + ) + .forEach { raw -> + val parsed = parseInvokeErrorMessage(raw) + assertEquals("UNAVAILABLE", parsed.code) + assertEquals(raw, parsed.message) + assertFalse(parsed.hadExplicitCode) + } } @Test