Skip to content

Commit f063e57

Browse files
ImLukeFEffetfrankekn
authored
fix(macos): use foundationValue when serializing browser proxy POST body (#43069)
Merged via squash. Prepared head SHA: 04c33fa Co-authored-by: ImLukeF <[email protected]> Co-authored-by: frankekn <[email protected]> Reviewed-by: @frankekn
1 parent 2d91284 commit f063e57

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ Docs: https://docs.openclaw.ai
165165
- Auth/profile resolution: log debug details when auto-discovered auth profiles fail during provider API-key resolution, so `--debug` output surfaces the real refresh/keychain/credential-store failure instead of only the generic missing-key message. (#41271) thanks @he-yufeng.
166166
- ACP/cancel scoping: scope `chat.abort` and shared-session ACP event routing by `runId` so one session cannot cancel or consume another session's run when they share the same gateway session key. (#41331) Thanks @pejmanjohn.
167167
- SecretRef/models: harden custom/provider secret persistence and reuse across models.json snapshots, merge behavior, runtime headers, and secret audits. (#42554) Thanks @joshavant.
168+
- macOS/browser proxy: serialize non-GET browser proxy request bodies through `AnyCodable.foundationValue` so nested JSON bodies no longer crash the macOS app with `Invalid type in JSON write (__SwiftValue)`. (#43069) Thanks @Effet.
168169

169170
## 2026.3.7
170171

apps/macos/Sources/OpenClaw/NodeMode/MacNodeBrowserProxy.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ actor MacNodeBrowserProxy {
146146
request.setValue(password, forHTTPHeaderField: "x-openclaw-password")
147147
}
148148

149-
if method != "GET", let body = params.body?.value {
150-
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [.fragmentsAllowed])
149+
if method != "GET", let body = params.body {
150+
request.httpBody = try JSONSerialization.data(withJSONObject: body.foundationValue, options: [.fragmentsAllowed])
151151
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
152152
}
153153

apps/macos/Tests/OpenClawIPCTests/MacNodeBrowserProxyTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,49 @@ struct MacNodeBrowserProxyTests {
3838
#expect(tabs.count == 1)
3939
#expect(tabs[0]["id"] as? String == "tab-1")
4040
}
41+
42+
// Regression test: nested POST bodies must serialize without __SwiftValue crashes.
43+
@Test func postRequestSerializesNestedBodyWithoutCrash() async throws {
44+
actor BodyCapture {
45+
private var body: Data?
46+
47+
func set(_ body: Data?) {
48+
self.body = body
49+
}
50+
51+
func get() -> Data? {
52+
self.body
53+
}
54+
}
55+
56+
let capturedBody = BodyCapture()
57+
let proxy = MacNodeBrowserProxy(
58+
endpointProvider: {
59+
MacNodeBrowserProxy.Endpoint(
60+
baseURL: URL(string: "http://127.0.0.1:18791")!,
61+
token: nil,
62+
password: nil)
63+
},
64+
performRequest: { request in
65+
await capturedBody.set(request.httpBody)
66+
let url = try #require(request.url)
67+
let response = try #require(
68+
HTTPURLResponse(
69+
url: url,
70+
statusCode: 200,
71+
httpVersion: nil,
72+
headerFields: nil))
73+
return (Data(#"{"ok":true}"#.utf8), response)
74+
})
75+
76+
_ = try await proxy.request(
77+
paramsJSON: #"{"method":"POST","path":"/action","body":{"nested":{"key":"val"},"arr":[1,2]}}"#)
78+
79+
let bodyData = try #require(await capturedBody.get())
80+
let parsed = try #require(JSONSerialization.jsonObject(with: bodyData) as? [String: Any])
81+
let nested = try #require(parsed["nested"] as? [String: Any])
82+
#expect(nested["key"] as? String == "val")
83+
let arr = try #require(parsed["arr"] as? [Any])
84+
#expect(arr.count == 2)
85+
}
4186
}

0 commit comments

Comments
 (0)