From 12213fcbfe3609766667441f70a3d51791761bbe Mon Sep 17 00:00:00 2001 From: Krishna Permi <40313959+krishnapermi@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:44:09 +0530 Subject: [PATCH 1/2] Add request context to unexpectedStatusCode and CustomStringConvertible conformance Adds the originating HTTPRequest to the unexpectedStatusCode error case so callers can surface the registry hostname in diagnostics. Also adds CustomStringConvertible to produce user-friendly messages like "Authentication failed for registry \"ghcr.io\": the server returned HTTP 401." --- Sources/ContainerRegistry/HTTPClient.swift | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Sources/ContainerRegistry/HTTPClient.swift b/Sources/ContainerRegistry/HTTPClient.swift index c9bee70..23cfe78 100644 --- a/Sources/ContainerRegistry/HTTPClient.swift +++ b/Sources/ContainerRegistry/HTTPClient.swift @@ -23,7 +23,7 @@ import HTTPTypesFoundation /// /// The response to a `HEAD` request does not include a body so if an error is thrown, `data` will be `nil` public enum HTTPClientError: Error { - case unexpectedStatusCode(status: HTTPResponse.Status, response: HTTPResponse, data: Data?) + case unexpectedStatusCode(request: HTTPRequest, status: HTTPResponse.Status, response: HTTPResponse, data: Data?) case unexpectedContentType(String) case missingContentType case missingResponseHeader(String) @@ -85,9 +85,9 @@ extension URLSession: HTTPClient { // A HEAD request has no response body and cannot be decoded if request.method == .head { - throw HTTPClientError.unexpectedStatusCode(status: response.status, response: response, data: nil) + throw HTTPClientError.unexpectedStatusCode(request: request, status: response.status, response: response, data: nil) } - throw HTTPClientError.unexpectedStatusCode(status: response.status, response: response, data: responseData) + throw HTTPClientError.unexpectedStatusCode(request: request, status: response.status, response: response, data: responseData) } return response @@ -135,6 +135,32 @@ extension URLSession: HTTPClient { } } +extension HTTPClientError: CustomStringConvertible { + /// Human-readable description of an HTTPClientError, including the registry hostname where available. + public var description: String { + switch self { + case let .unexpectedStatusCode(request, status, _, _): + let host = request.url?.host ?? "unknown registry" + if status == .unauthorized || status == .forbidden { + return "Authentication failed for registry \"\(host)\": the server returned HTTP \(status.code). Ensure your credentials are correct." + } + return "Registry \"\(host)\" returned an unexpected HTTP \(status.code) response." + case let .unexpectedContentType(type): + return "Unexpected content type: \(type)" + case .missingContentType: + return "Response is missing a content type" + case let .missingResponseHeader(header): + return "Response is missing the '\(header)' header" + case let .authenticationChallenge(_, request, _): + let host = request.url?.host ?? "unknown registry" + return "Registry \"\(host)\" requires authentication." + case let .unauthorized(request, _): + let host = request.url?.host ?? "unknown registry" + return "Authentication failed for registry \"\(host)\": credentials were rejected." + } + } +} + extension HTTPRequest { /// Constructs a HTTPRequest pre-configured with method, url and content types. /// - Parameters: From 2d4725e0bb3d2a4672a1799957ee3f5b9dbdabaa Mon Sep 17 00:00:00 2001 From: Krishna Permi <40313959+krishnapermi@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:47:06 +0530 Subject: [PATCH 2/2] Fix catch clauses for unexpectedStatusCode after request parameter addition The unexpectedStatusCode enum case now carries a leading request parameter. Update the two catch patterns in RegistryClient to bind a wildcard for it so the code continues to compile. --- Sources/ContainerRegistry/RegistryClient.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ContainerRegistry/RegistryClient.swift b/Sources/ContainerRegistry/RegistryClient.swift index 474ab40..c3d829e 100644 --- a/Sources/ContainerRegistry/RegistryClient.swift +++ b/Sources/ContainerRegistry/RegistryClient.swift @@ -369,7 +369,7 @@ extension RegistryClient { do { return try await client.executeRequestThrowing(request, expectingStatus: success) - } catch HTTPClientError.unexpectedStatusCode(let status, _, let .some(responseData)) + } catch HTTPClientError.unexpectedStatusCode(_, let status, _, let .some(responseData)) where errors.contains(status) { // Try to decode as JSON; if that fails, throw a generic error with the raw response body @@ -416,7 +416,7 @@ extension RegistryClient { do { return try await client.executeRequestThrowing(request, uploading: payload, expectingStatus: success) - } catch HTTPClientError.unexpectedStatusCode(let status, _, let .some(responseData)) + } catch HTTPClientError.unexpectedStatusCode(_, let status, _, let .some(responseData)) where errors.contains(status) { // Try to decode as JSON; if that fails, throw a generic error with the raw response body