Hmbown/CodeWhale · error · NSException
%s
Error message
%s
What it means
This Objective-C computer-use backend serializes the command's result dictionary with NSJSONSerialization (fragments allowed) and prints it to stdout as the command's output contract. If serialization fails — typically because the result contains an object NSJSONSerialization cannot encode, such as an NSNumber holding NaN/inf, a non-string dictionary key, or a cycle — it throws an NSException named "json" carrying the underlying NSError's localizedDescription. The catch block releases the input lease and exits 1 after writing the reason to stderr.
Solutions
- Read the stderr message (the localizedDescription) to identify the offending value type; locate the command that built the result and sanitize its new field (map NaN/inf to NSNull or 0, coerce keys to NSString).
- Audit the failing code path for numeric results coming from CoreGraphics/AX APIs that can return NaN (e.g. kCGNullWindowID or zero-rect queries) and guard them before inserting into the result.
- If you own the plugin, add a pre-serialization validity check with NSJSONSerialization isValidJSONObject: and log the result dictionary keys to pinpoint the bad entry.
- As a workaround while debugging, serialize individual fields to find which one fails before fixing the source.
Example fix
// before ack[@"cursor"] = [NSNumber numberWithDouble:NAN]; // from a failed position query NSData *data=[NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingFragmentsAllowed error:&error]; // throws NSException "json" // after double pos = queryCursor(); ack[@"cursor"] = isfinite(pos) ? @(pos) : NSNull.null; NSData *data=[NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingFragmentsAllowed error:&error];
Defensive patterns
Strategy: try-catch
Validate before calling
// In the command handler, before serialization:
if (![NSJSONSerialization isValidJSONObject:result]) {
NSLog(@"non-JSON result keys: %@", [result allKeys]);
}
for (id v in [result allValues]) {
if ([v isKindOfClass:[NSNumber class]] &&
(isnan([v doubleValue]) || isinf([v doubleValue]))) {
// replace with NSNull or a sentinel before building result
}
} Type guard
static inline BOOL CuJSONEncodable(id v) {
if ([v isKindOfClass:[NSNumber class]]) {
double d = [v doubleValue];
return !(isnan(d) || isinf(d));
}
return [NSJSONSerialization isValidJSONObject:@[ v ]] || v == NSNull.null;
} Try / catch
@try {
NSData *data=[NSJSONSerialization dataWithJSONObject:result
options:NSJSONWritingFragmentsAllowed error:&error];
if(!data) NSLog(@"serialize failed: %@", error.localizedDescription);
} @catch(NSException *e){
// e.name == @"json" — sanitize result and retry once with NSNull substitution
} Prevention
- Run NSJSONSerialization isValidJSONObject: on every newly added result field during development.
- Never insert raw doubles from geometry/AX APIs without an isfinite check.
- Keep result dictionary keys NSString-only.
- Test every new backend command end-to-end on macOS before shipping, since the exception only surfaces at runtime.
When it happens
Trigger: Any backend command (invoked with args containing input_lease) whose constructed `result` dictionary contains values NSJSONSerialization dataWithJSONObject:options:error: rejects: NaN or infinite floats, NSNull used as a dictionary key, NSDecimalNumber in exotic states, or a mutated result object that introduced a retain cycle. Not triggered by user input alone — only by a backend bug producing a non-JSON-encodable value.
Common situations: A newly added command or field in the darwin-accessibility backend returns a value type not JSON-encodable (e.g. a computed NaN coordinate from a failed CGEvent or accessibility geometry query); a macOS API update returns nil-backed objects that become invalid NSNumbers; a developer extends the result dict with a non-NSString key.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- <dynamic: wrapped serde_json serialization error>
- <dynamic: wrapped serde_json serialization error>
- {error}
- Failed to parse MCP config
- Failed to render MCP template JSON
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/fc778577fad6f7ef.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin-accessibility.m:1804
if(e!=kAXErrorSuccess) @throw [NSException exceptionWithName:@"action" reason:[NSString stringWithFormat:@"accessibility action failed: %d",e] userInfo:nil];
NSMutableDictionary *done=[@{@"action_sent":@YES,@"strategy":@"a11y"} mutableCopy];
if([tool isEqual:@"focus_element"]) done[@"focused"]=@YES;
return done;
} @finally { CFRelease(app); }
}
int main(int argc, const char **argv){ @autoreleasepool {
signal(SIGTERM,cuCancel); signal(SIGINT,cuCancel); signal(SIGPIPE,SIG_IGN);
@try { if(argc!=2) @throw [NSException exceptionWithName:@"args" reason:@"expected one JSON argument" userInfo:nil];
NSError *error=nil; id p=[NSJSONSerialization JSONObjectWithData:[[NSString stringWithUTF8String:argv[1]] dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
if(![p isKindOfClass:NSDictionary.class]) @throw [NSException exceptionWithName:@"json" reason:@"invalid request" userInfo:nil];
id result=execute(p);
if([p[@"args"][@"input_lease"] boolValue]) { NSMutableDictionary *ack=[result mutableCopy]; ack[@"input_lease"]=@YES; result=ack; }
NSData *data=[NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingFragmentsAllowed error:&error];
if(!data) @throw [NSException exceptionWithName:@"json" reason:error.localizedDescription userInfo:nil];
puts([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding].UTF8String); fflush(stdout);
if([p[@"args"][@"input_lease"] boolValue]) cuWaitForLease();
return 0;
} @catch(NSException *e){ cuReleaseLease(); fprintf(stderr,"%s\n",e.reason.UTF8String); return 1; }
} }
View on GitHub (pinned to 73e0f67d83)