Fix the JSONSerialization crash risk before release
JSONSerialization.data(withJSONObject:) looks like a normal throwing API, but for a genuinely invalid top-level object (Double.nan/ .infinity anywhere in the object graph, or a non-bridgeable type) it raises an Objective-C exception instead of a catchable Swift Error — neither try nor try? protects against that, and the process crashes. Logged as a priority roadmap item after being found while building Apple Intelligence tool calling; fixing it now before release per Rune's ask. SafeJSONEncoding (new) checks JSONSerialization.isValidJSONObject first — a plain, safe, non-throwing Bool check — before ever calling the crash-prone encode path, returning nil instead of crashing for invalid input. Verified the check itself can't be fooled (a standalone script confirmed it correctly predicts NaN, Infinity, nested NaN, and non-bridgeable-type cases, all without crashing) before wiring it in. Applied to every call site that encodes data the app doesn't fully control — tool results (ChatViewModel.generateAIResponseWithTools, DynamicMCPTool.encodeResult, MCPService.serializeToolResult for research sub-agents) and MessageRow's tool-call-detail pretty-printer. Left outbound request-body construction in the provider files alone — those dictionaries are built entirely from known Swift types the app already controls (validated settings, string content), not from tool/model/external-server output, so the same crash class isn't realistically reachable there. Also restored a test that previously had to be deliberately skipped because it reproducibly crashed the whole test process on this exact bug (documented at the time in feature_apple_intelligence_provider memory) — now passes cleanly, directly confirming the fix rather than just the new code compiling. 436 tests total, stable across two consecutive full runs.
This commit is contained in:
@@ -1849,7 +1849,7 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
|
||||
let result = await mcp.executeTool(name: tc.functionName, arguments: tc.arguments, agentProvider: provider, agentModelId: effectiveModelId)
|
||||
let resultJSON: String
|
||||
if let data = try? JSONSerialization.data(withJSONObject: result),
|
||||
if let data = SafeJSONEncoding.data(withJSONObject: result),
|
||||
let str = String(data: data, encoding: .utf8) {
|
||||
// Cap tool results at 50 KB to avoid HTTP 413 on the next API call
|
||||
let maxBytes = 50_000
|
||||
|
||||
Reference in New Issue
Block a user