From e9b208a58592b726e850cf9a8513da6e70f9eb6e Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 17 Mar 2026 17:10:41 +0000 Subject: [PATCH 1/2] fix: Allow POST requests without Content-Type header when body is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per RFC 9110 §8.3, Content-Type header is only meaningful when a message body is present. The HTTP+JSON transport was incorrectly returning 415 (ContentTypeNotSupportedError) for POST requests with no body and no Content-Type header. This fix adds validateContentTypeForOptionalBody() method to handle endpoints like POST /tasks/{id}:cancel where the body is optional. When the body is null or empty, Content-Type validation is skipped. When body content is present, Content-Type must still be application/json. This allows bodyless POST requests to be processed correctly and return appropriate errors (404 TaskNotFoundError, 409 TaskNotCancelableError) instead of 415. Fixes #747 Co-Authored-By: Claude Sonnet 4.5 --- .../server/rest/quarkus/A2AServerRoutes.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 19399c18a..daaf0d921 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -346,7 +346,7 @@ public void getTask(RoutingContext rc) { */ @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void cancelTask(@Body String body, RoutingContext rc) { - if (!validateContentType(rc)) { + if (!validateContentTypeForOptionalBody(rc, body)) { return; } String taskId = rc.pathParam("taskId"); @@ -624,6 +624,25 @@ private boolean validateContentType(RoutingContext rc) { return true; } + /** + * Check if the request content type is application/json when body content is present. + * Per RFC 9110 §8.3, Content-Type is only meaningful when a message body is present. + * Use this for endpoints where the body is optional. + * + * @param rc the routing context + * @param body the request body (may be null or empty) + * @return true if validation passes, false if Content-Type error should be returned + */ + private boolean validateContentTypeForOptionalBody(RoutingContext rc, @Nullable String body) { + // If body is null or empty, Content-Type is not required + if (body == null || body.trim().isEmpty()) { + return true; + } + + // Body has content - validate Content-Type + return validateContentType(rc); + } + /** * Retrieves the public agent card for agent discovery. * From 54495ddd723097d3be2785c95168d5e73bd09b60 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 17 Mar 2026 17:18:14 +0000 Subject: [PATCH 2/2] Update reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index daaf0d921..11f5ab299 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -635,7 +635,7 @@ private boolean validateContentType(RoutingContext rc) { */ private boolean validateContentTypeForOptionalBody(RoutingContext rc, @Nullable String body) { // If body is null or empty, Content-Type is not required - if (body == null || body.trim().isEmpty()) { + if (body == null || body.isBlank()) { return true; }