External Command Output Parsing#
Tuist parses JSON and header payloads from external system commands (primarily simctl and devicectl) and HTTP responses. Two code paths are known to surface a raw NSCocoaErrorDomain Code=3840 "Unable to parse empty data." instead of a typed Tuist error when input is empty or malformed. This is an open hardening gap tracked in issue #13747.
simctl JSON β SimulatorController#
SimulatorController.devices() and runtimes() run xcrun simctl list β¦ --json and feed stdout directly to JSONSerialization.jsonObject. If simctl exits 0 with empty stdout, JSONSerialization throws the raw 3840 error; there is no surrounding catch block to convert it to a SimulatorControllerError .
SimulatorControllerError has three typed cases β simctlError(String), deviceNotFound, simulatorNotFound(udid:) β but none for a JSON parse failure . The recommended fix from the issue report is to catch empty or non-JSON stdout and throw a typed simctlError that includes the command, exit status, and stderr.
devicectl JSON β DeviceController#
DeviceController.findAvailableDevices() writes devicectl list devices --json-output to a temporary file, then wraps the JSONDecoder call in a do/catch that converts any failure β including JSON parse errors β to DeviceControllerError.fetchingDevicesFailed . This surfaces a clean user-facing message ("Fetching the list of devices failed.") but discards the underlying decode error, making it hard to diagnose root causes (see also issue #12052).
HTTP x-tuist-warnings Header β OutputWarningsMiddleware#
OutputWarningsMiddleware reads the x-tuist-warnings response header (base64-encoded JSON array of warning strings) and logs each warning via AlertController. The bug: Data(base64Encoded: "") returns a non-nil, zero-byte Data value, so the couldntConvertToData guard passes and JSONSerialization.jsonObject throws 3840 for an empty header value . The fix is to treat an empty header value as "no warnings" and return early before decoding.
Error Types Reference#
| Error type | File | Thrown when |
|---|---|---|
SimulatorControllerError.simctlError(String) | TuistCore/Simulator/SimulatorController.swift | simctl process error; not currently thrown on JSON parse failure |
DeviceControllerError.fetchingDevicesFailed | TuistAutomation/Device/DeviceController.swift | Any error during devicectl JSON decode |
OutputWarningsMiddlewareError.couldntConvertToData | TuistHTTP/OutputWarningsMiddleware.swift | Base64 decode fails (but misses the empty-string case) |
OutputWarningsMiddlewareError.invalidSchema | TuistHTTP/OutputWarningsMiddleware.swift | JSON array schema mismatch |
Key Files#
cli/Sources/TuistCore/Simulator/SimulatorController.swiftβsimctlinvocation and JSON parsingcli/Sources/TuistAutomation/Device/DeviceController.swiftβdevicectlinvocation and JSON parsing with error wrappingcli/Sources/TuistHTTP/OutputWarningsMiddleware.swiftβ HTTP warning header parsing- GitHub Issue #13747 β root-cause analysis and suggested fixes