Fix cryptic error for pairing files missing required fields

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAnD7odyVzX36xY1vvZcJL
This commit is contained in:
Claude 2026-07-14 13:25:49 +00:00
parent abcc0cfc78
commit 2ee7a0c71f
No known key found for this signature in database

View file

@ -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) }