OpenAPITools / OpenAPITools/openapi-generator
[BUG][SWIFT] Incorrect Optional Type Handling for 204 No Content Responses in Generated API Client
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to Speed Up the Bug Fix or Feature Request
Description
When generating a Swift client from an OpenAPI specification that includes a 204 No Content response, the generated code appropriately changes the type for the 200 OK response to be optional (e.g., Data?). However, there is an issue with how the processRequestResponse method handles this optional type.
Specifically, in the processRequestResponse method, the switch statement is intended to match the expected response type (T). However, when the type is Optional (or any other optional type), the switch cases do not correctly handle this scenario. As a result, the execution falls through to the default case. In this default case, the method attempts to decode the response data as JSON, which is incorrect and can lead to runtime errors.
OpenAPI Generator Version
openapi-generator version: 7.7.0
OpenAPI Declaration File Content or URL
{
"/example/path/download/{fileId}": {
"get": {
"operationId": "downloadFile",
"parameters": [
{
"name": "fileId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful file download",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"204": {
"description": "No content available"
}
}
}
}
}
Example of Generated Code
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T> {
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
if let error = error {
completion(.failure(ErrorResponse.error(-1, data, response, error)))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
return
}
guard httpResponse.isStatusCodeSuccessful else {
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
return
}
switch T.self {
case is String.Type:
let body = data.flatMap { String(data: $0, encoding: .utf8) } ?? ""
completion(.success(Response<T>(response: httpResponse, body: body as! T, bodyData: data)))
case is Data.Type:
completion(.success(Response(response: httpResponse, body: data as! T, bodyData: data)))
default:
let decodeResult = CodableHelper.decode(T.self, from: unwrappedData)
switch decodeResult {
case let .success(decodableObj):
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
case let .failure(error):
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
}
}
}
}
Steps to Reproduce
- Create an OpenAPI specification with an endpoint that returns a binary file and includes a 204 No Content response.
- Generate the Swift client using OpenAPI Generator.
- Attempt to process a response from the endpoint and observe that the code falls through to the default case, leading to an incorrect attempt to decode the response as JSON.
Actual vs. Expected Output
- Actual Output:
The generated Swift client code fails to match Optional in the switch case and attempts to decode the response as JSON, resulting in errors. - Expected Output:
The generated Swift client code should correctly match Optional in the switch case and handle it appropriately without attempting to decode it as JSON.
Related Issues/PRs
None found.
Suggest a fix
I would like to firstly raise a valid point: handling empty responses, particularly with 204 No Content, is nuanced because different types (Data, String, etc.) might need different approaches.
- Data Type: When a 204 No Content response is received, the data might be an empty byte array, which is technically still valid but semantically could indicate that no actual content is available. This might be treated as an error if non-empty content is expected.
- String Type: An empty string is valid, but if a 204 No Content response is returned and the type is Optional, it might be misleading to treat an empty string as a valid response. In such cases, returning an error instead of an empty string could be more appropriate.
Given that this issue with 204 No Content responses could affect other types beyond just Data and String, it may be beneficial to conduct a broader review of how the generated code handles optional types across different scenarios.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the generated Swift URLSessionDecodableRequestBuilder processRequestResponse method shown in the report and reproduce the binary-response case with a 204 response. Trace how Optional reaches the switch and verify that successful empty responses avoid JSON decoding. Done means the generated client handles the reported Optional case correctly, with coverage for the relevant response behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100