Component
SDKs
Priority
P2 - Medium (Would be helpful)
SDK Language(s)
PHP, Go, Ruby
Summary
Endpoints with no request body at all (a bare DELETE, for example) still send Content-Type: application/json with an empty body in the PHP, Go, and Ruby SDKs. Strict servers reject that combination before the route handler runs — Fastify throws FST_ERR_CTP_EMPTY_JSON_BODY:
{
"statusCode": 400,
"code": "FST_ERR_CTP_EMPTY_JSON_BODY",
"message": "Body cannot be empty when content-type is set to 'application/json'"
}
The respectOptionalRequestBody option (fern-php-sdk 2.20.0) fixes the adjacent case — an optional body the caller omits — but does not reach this one: generators/php/sdk/src/endpoint/utils/mayOmitRequestBody.ts requires requestBody?.type === "reference" && requestBody.required === false, so an endpoint with requestBody === undefined never gets omitContentTypeWithoutBody: true from HttpEndpointGenerator, and the default header still goes out even on 2.20.4 with the flag enabled.
Repro
OpenAPI operation: a DELETE with path parameters and no requestBody. Generated PHP client code:
$response = $this->client->sendRequest(
new JsonApiRequest(
baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
path: "v1/tenants/{$tenantId}/documents/{$id}",
method: HttpMethod::DELETE,
),
$options,
);
RawClient::encodeHeaders() stamps Content-Type: application/json for every JsonApiRequest regardless of body; encodeRequestBody() correctly attaches nothing. On the wire:
DELETE /v1/tenants/local/documents/{id} HTTP/1.1
Content-Type: application/json
(no body)
Affected generators
- PHP —
generators/php/base/src/asIs/Client/RawClient.Template.php: encodeHeaders() sets the default Content-Type unconditionally for JsonApiRequest/UrlEncodedApiRequest. The header-drop logic added for respectOptionalRequestBody is gated on the per-request $request->omitContentTypeWithoutBody, which is only emitted as true for optional referenced bodies.
- Go — generated
internal/caller.go: req.Header.Set(contentTypeHeader, reqContentType) runs even when newRequestBody(...) returned nil for a bodyless request.
- Ruby — generated
internal/json/request.rb: encode_headers always includes "Content-Type" => "application/json", while encode_body returns nil.
TypeScript, Python, Java, and C# already behave correctly (no body → no Content-Type).
Ask
When no body is attached to the outgoing request, don't emit the default Content-Type. For bodyless endpoints this involves no method-signature change, so it could arguably be unconditional (a wire-only correction); at minimum, extend respectOptionalRequestBody so mayOmitRequestBody also returns true when endpoint.requestBody is absent — in the PHP, Go, and Ruby generators alike.
A Content-Type explicitly supplied by the caller should keep going out, matching the behavior of the existing drop logic.
Versions
No response
Workaround
No response
Are you interested in contributing a fix?
No
Component
SDKs
Priority
P2 - Medium (Would be helpful)
SDK Language(s)
PHP, Go, Ruby
Summary
Endpoints with no request body at all (a bare
DELETE, for example) still sendContent-Type: application/jsonwith an empty body in the PHP, Go, and Ruby SDKs. Strict servers reject that combination before the route handler runs — Fastify throwsFST_ERR_CTP_EMPTY_JSON_BODY:{ "statusCode": 400, "code": "FST_ERR_CTP_EMPTY_JSON_BODY", "message": "Body cannot be empty when content-type is set to 'application/json'" }The
respectOptionalRequestBodyoption (fern-php-sdk 2.20.0) fixes the adjacent case — an optional body the caller omits — but does not reach this one:generators/php/sdk/src/endpoint/utils/mayOmitRequestBody.tsrequiresrequestBody?.type === "reference" && requestBody.required === false, so an endpoint withrequestBody === undefinednever getsomitContentTypeWithoutBody: truefromHttpEndpointGenerator, and the default header still goes out even on 2.20.4 with the flag enabled.Repro
OpenAPI operation: a
DELETEwith path parameters and norequestBody. Generated PHP client code:RawClient::encodeHeaders()stampsContent-Type: application/jsonfor everyJsonApiRequestregardless of body;encodeRequestBody()correctly attaches nothing. On the wire:Affected generators
generators/php/base/src/asIs/Client/RawClient.Template.php:encodeHeaders()sets the defaultContent-Typeunconditionally forJsonApiRequest/UrlEncodedApiRequest. The header-drop logic added forrespectOptionalRequestBodyis gated on the per-request$request->omitContentTypeWithoutBody, which is only emitted astruefor optional referenced bodies.internal/caller.go:req.Header.Set(contentTypeHeader, reqContentType)runs even whennewRequestBody(...)returnednilfor a bodyless request.internal/json/request.rb:encode_headersalways includes"Content-Type" => "application/json", whileencode_bodyreturnsnil.TypeScript, Python, Java, and C# already behave correctly (no body → no
Content-Type).Ask
When no body is attached to the outgoing request, don't emit the default
Content-Type. For bodyless endpoints this involves no method-signature change, so it could arguably be unconditional (a wire-only correction); at minimum, extendrespectOptionalRequestBodysomayOmitRequestBodyalso returnstruewhenendpoint.requestBodyis absent — in the PHP, Go, and Ruby generators alike.A
Content-Typeexplicitly supplied by the caller should keep going out, matching the behavior of the existing drop logic.Versions
No response
Workaround
No response
Are you interested in contributing a fix?
No