From 327d65806994ff0c44c95fd32e276782cfa05dec Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:53:00 -0800 Subject: [PATCH] Fix CLI v2 commands showing JSON parse error instead of actual error (#189) When the server returns a plain-text error (e.g., "ERROR: Access denied ...") before the JSON protocol starts, sendV2() would pass it through JSONSerialization which throws a confusing NSCocoaErrorDomain 3840 error. Now sendV2() checks for "ERROR:" prefix and surfaces the real message. Also includes the raw response in the fallback error for easier debugging. Fixes https://github.com/manaflow-ai/cmux/issues/188 --- CLI/cmux.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CLI/cmux.swift b/CLI/cmux.swift index 9220afd8..bdb617b4 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -365,11 +365,19 @@ final class SocketClient { } let raw = try send(command: requestLine) + + // The server may return plain-text errors (e.g., "ERROR: Access denied ...") + // before the JSON protocol starts. Surface these directly instead of letting + // JSONSerialization throw a confusing parse error. + if raw.hasPrefix("ERROR:") { + throw CLIError(message: raw) + } + guard let responseData = raw.data(using: .utf8) else { throw CLIError(message: "Invalid UTF-8 v2 response") } guard let response = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: Any] else { - throw CLIError(message: "Invalid v2 response") + throw CLIError(message: "Invalid v2 response: \(raw)") } if let ok = response["ok"] as? Bool, ok {