microsoftgraph / microsoftgraph/msgraph-sdk-java
JsonSyntaxException in BatchResponseContent when batch response contains non-JSON body
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Java
- Star
- 444
- Fork
- 154
- Merge trung bình
- 18 giờ 28 phút
- Pull request đã merge (30 ngày)
- 4
Mô tả
Describe the bug
When making a batch request using the Microsoft Graph Java SDK, the SDK fails to parse the batch response if one of the individual responses contains a plain text body instead of valid JSON.
com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 149 path $.responses[1].body
See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json
at com.google.gson.internal.Streams.parse(Streams.java:58) ~[gson-2.11.0.jar!/:na]
at com.google.gson.JsonParser.parseReader(JsonParser.java:146) ~[gson-2.11.0.jar!/:na]
at com.google.gson.JsonParser.parseReader(JsonParser.java:110) ~[gson-2.11.0.jar!/:na]
at com.microsoft.graph.core.content.BatchResponseContent.getBatchResponseContent(BatchResponseContent.java:168) ~[microsoft-graph-core-3.6.1.jar!/:na]
...
Caused by: com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 149 path $.responses[1].body
The raw batch response returned by the API looks like this (simplified for clarity):
{
"responses": [
{
"id": "a063d4a4-XXXX-4433-8704-6b1252f7e864",
"status": 200,
"headers": {
"Cache-Control": "private",
"Content-Type": "application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false; charset=utf-8"
},
"body": {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('user%40domain.com')/events(id)",
"value": [
{
"@odata.etag": "W/\"a+n3FpwGr06xxxxxxxxxjmCBjQ==\"",
"id": "AAMkAGVlYjJjNGxxxxxxxABGAAAAAAAkmk-HOxjKRZmNlLfGqYjaBwBr6fcWnAavTpXG0aD="
}
]
}
},
{
"id": "5f956687-XXXX-4aa4-a726-dc6b9810db1e",
"status": 503,
"headers": {
"Cache-Control": "private"
},
"body": Authentication Concurrency Limit Reached
}
]
}
As you can see, the second response has a body field that is not valid JSON (Authentication Concurrency Limit Reached), which causes the SDK to throw a JsonSyntaxException when parsing.
There is also another variant that falls into the same error:
{
"responses": [
{
"id": "6ea85c49-XXXX-4116-9844-2b0cbbf52ea3",
"status": 503,
"headers": {
"Content-Type": "text/html; charset=us-ascii"
},
"body":<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Service Unavailable</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Application Request Queue Full</h2>
<hr><p>HTTP Error 503. The application request queue is full.</p>
</BODY></HTML>
}
]
}
Expected behavior
The SDK should be able to handle non-JSON response bodies gracefully in batch responses. For example:
- Return the raw string body when the response is not valid JSON.
- Or provide a way to detect and handle such cases without throwing a parsing exception.
How to reproduce
- Use the
GraphServiceClientto send multiple requests (20 request per batch) in parallel (+4 in parallel) usingGraphServiceClient().getBatchRequestBuilder().post() - When the API is under load, one of the requests may return a 503 Authentication Concurrency Limit Reached error.
- The batch response will then contain a body field with plain text instead of valid JSON.
- Attempting to parse the batch response with BatchResponseContent.getResponseById(...) will throw a JsonSyntaxException.
To see the raw API response add the following interceptor to the OkHttpClient
@Slf4j
public class RawResponseLoggingInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
if (responseBody != null) {
try {
MediaType contentType = responseBody.contentType();
String rawJson = responseBody.string();
log.info("[API] Raw JSON Response for URL [{}], Status [{}]: {}", request.url(), response.code(), rawJson);
ResponseBody newResponseBody = ResponseBody.create(rawJson, contentType);
return response.newBuilder().body(newResponseBody).build();
} catch (Exception e) {
log.error("[API] Error reading raw response body for URL [{}]", request.url(), e);
}
}
return response;
}
}
RawResponseLoggingInterceptor loggingInterceptor = new RawResponseLoggingInterceptor();
OkHttpClient customOkHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();
GraphServiceClient client = new GraphServiceClient(authProvider, customOkHttpClient);
SDK Version
6.51.0
Latest version known to work for scenario above?
No response
Known Workarounds
No response
Debug output
Click to expand log
```com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 149 path $.responses[0].body
See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json
at com.google.gson.internal.Streams.parse(Streams.java:58) ~[gson-2.11.0.jar!/:na]
at com.google.gson.JsonParser.parseReader(JsonParser.java:146) ~[gson-2.11.0.jar!/:na]
at com.google.gson.JsonParser.parseReader(JsonParser.java:110) ~[gson-2.11.0.jar!/:na]
at com.microsoft.graph.core.content.BatchResponseContent.getBatchResponseContent(BatchResponseContent.java:168) ~[microsoft-graph-core-3.6.1.jar!/:na]
at com.microsoft.graph.core.content.BatchResponseContent.getResponseById(BatchResponseContent.java:94) ~[microsoft-graph-core-3.6.1.jar!/:na]
at java.base/java.util.HashMap.forEach(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.6.jar!/:6.2.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.6.jar!/:6.2.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.6.jar!/:6.2.6]
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:114) ~[spring-aop-6.2.6.jar!/:6.2.6]
at java.base/java.util.concurrent.FutureTask.run(Unknown Source) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
at java.base/java.lang.Thread.run(Unknown Source) ~[na:na]
Caused by: com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 149 path $.responses[0].body
See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1754) ~[gson-2.11.0.jar!/:na]
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:574) ~[gson-2.11.0.jar!/:na]
at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:498) ~[gson-2.11.0.jar!/:na]
at com.google.gson.internal.bind.TypeAdapters$28.read(TypeAdapters.java:875) ~[gson-2.11.0.jar!/:na]
at com.google.gson.internal.bind.TypeAdapters$28.read(TypeAdapters.java:820) ~[gson-2.11.0.jar!/:na]
at com.google.gson.internal.Streams.parse(Streams.java:46) ~[gson-2.11.0.jar!/:na]
... 18 common frames omitted
</details>
### Configuration
- OS: Windows 11 Pro 24H2
- Architecture: x64
- JDK: OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
### Other information
_No response_
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu trong com.microsoft.graph.core.content.BatchResponseContent, đặc biệt là getBatchResponseContent tại vị trí trong stack trace và getResponseById. Tái hiện việc phân tích với các batch body dạng văn bản thuần túy và HTML được hiển thị trong issue, sau đó xác minh rằng các response body không phải JSON không còn gây ra JsonSyntaxException và vẫn có thể được sử dụng để xử lý.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- java
- Lĩnh vực
- api
- Loại issue
- Lỗi
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 48/100