From 2ee7a0c71f56f6c1eb3b47c9d3f9b913b95e48cb Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 13:25:49 +0000 Subject: [PATCH] Fix cryptic error for pairing files missing required fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When rp_pairing_file_read fails to parse the local pairing plist (e.g. an older/incompatible file missing public_key), the raw Serde/plist debug string was surfaced as a generic "Connection Error" with a useless "Try Again" button — retrying a local parse failure can never succeed. Reclassify this failure to the same error code used for invalid pairing files so it routes to the existing "Select New File" prompt instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DAnD7odyVzX36xY1vvZcJL --- StikDebug/Device/JITEnableContext.swift | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/StikDebug/Device/JITEnableContext.swift b/StikDebug/Device/JITEnableContext.swift index a63c5e7..9448486 100644 --- a/StikDebug/Device/JITEnableContext.swift +++ b/StikDebug/Device/JITEnableContext.swift @@ -126,7 +126,8 @@ final class JITEnableContext { } if let ffiError { - throw error(from: ffiError, fallback: "Failed to read pairing file!") + let readError = error(from: ffiError, fallback: "Failed to read pairing file!") + throw Self.isMalformedPairingFile(readError) ? malformedPairingFileError(readError) : readError } guard let pairingFile else { @@ -136,6 +137,24 @@ final class JITEnableContext { return pairingFile } + /// `rp_pairing_file_read` only parses the local plist on disk — no network + /// involved — so a Serde/plist error here always means the file's contents + /// are the wrong shape (e.g. an older pairing file missing `public_key`), + /// not a transient failure a retry would fix. Reclassifying it to the same + /// code as an invalid pairing file routes it to the "select a new file" + /// prompt instead of the generic connection-error/retry flow. + private static func isMalformedPairingFile(_ error: NSError) -> Bool { + let message = error.localizedDescription.lowercased() + return message.contains("missing field") || message.contains("serde(") || message.contains("plist(error") + } + + private func malformedPairingFileError(_ underlying: NSError) -> NSError { + makeError( + "The pairing file is missing data StikDebug needs (\(underlying.localizedDescription)). It's likely the wrong format or an incomplete export — select a new pairing file.", + code: -9 + ) + } + private func createTunnel(hostname: String) throws -> TunnelHandles { let pairingFile = try getPairingFile() defer { rp_pairing_file_free(pairingFile) }