microsoftgraph / microsoftgraph/msgraph-sdk-java

JsonSyntaxException in BatchResponseContent when batch response contains non-JSON body

Ouverte
#2,472 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

status:waiting-for-triage type:bug
Langage dominant
Java
Étoiles
444
Forks
154
Merge moyen
18 h 28 min
PR mergées (30 j)
4

Description

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
  1. Use the GraphServiceClient to send multiple requests (20 request per batch) in parallel (+4 in parallel) using GraphServiceClient().getBatchRequestBuilder().post()
  2. When the API is under load, one of the requests may return a 503 Authentication Concurrency Limit Reached error.
  3. The batch response will then contain a body field with plain text instead of valid JSON.
  4. 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_

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans com.microsoft.graph.core.content.BatchResponseContent, en particulier dans getBatchResponseContent à l’emplacement indiqué par la stack trace et dans getResponseById. Reproduisez l’analyse avec les corps de batch en texte brut et en HTML présentés dans l’issue, puis vérifiez que les corps de réponse qui ne sont pas au format JSON ne provoquent plus de JsonSyntaxException et restent disponibles pour être traités.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
java
Domaine
api
Type d'issue
Bug
Difficulté
3/5
Temps estimé
1-2 jours
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
48/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.