This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e52fb2c4a28db6f47e68ae314629f0353f8cdb36
Author: Tung Van TRAN <vtt...@linagora.com>
AuthorDate: Fri Jun 16 08:14:09 2023 +0700

    [CLEAN CODE] JMAP - Make standard error message when invalid json 
deserialize
---
 .../rfc8621/contract/DelegateSetContract.scala     |  4 +-
 .../contract/EmailQueryMethodContract.scala        | 16 +++----
 .../rfc8621/contract/IdentitySetContract.scala     |  4 +-
 .../contract/MailboxQueryMethodContract.scala      | 10 ++--
 .../contract/MailboxSetMethodContract.scala        | 12 ++---
 .../PushSubscriptionSetMethodContract.scala        |  8 ++--
 .../contract/QuotaChangesMethodContract.scala      |  4 +-
 .../contract/QuotaQueryMethodContract.scala        |  8 ++--
 .../apache/james/jmap/delegation/DelegateSet.scala | 15 ++----
 .../org/apache/james/jmap/mail/IdentitySet.scala   | 13 +-----
 .../scala/org/apache/james/jmap/mail/MDNSend.scala | 17 ++-----
 .../james/jmap/method/DelegateGetMethod.scala      | 10 ++--
 .../james/jmap/method/DelegateSetMethod.scala      |  8 +---
 .../jmap/method/DelegatedAccountGetMethod.scala    | 10 ++--
 .../jmap/method/DelegatedAccountSetMethod.scala    | 11 ++---
 .../james/jmap/method/EmailChangesMethod.scala     |  7 ++-
 .../apache/james/jmap/method/EmailGetMethod.scala  | 11 ++---
 .../james/jmap/method/EmailImportMethod.scala      |  8 ++--
 .../james/jmap/method/EmailParseMethod.scala       | 54 ++++++++++------------
 .../james/jmap/method/EmailQueryMethod.scala       | 11 ++---
 .../apache/james/jmap/method/EmailSetMethod.scala  |  6 +--
 .../jmap/method/EmailSubmissionSetMethod.scala     | 34 +++++---------
 .../james/jmap/method/IdentityChangesMethod.scala  |  8 ++--
 .../james/jmap/method/IdentityGetMethod.scala      |  5 +-
 .../james/jmap/method/IdentitySetMethod.scala      |  7 ++-
 .../apache/james/jmap/method/MDNParseMethod.scala  |  9 ++--
 .../apache/james/jmap/method/MDNSendMethod.scala   |  8 ++--
 .../james/jmap/method/MailboxChangesMethod.scala   |  8 ++--
 .../james/jmap/method/MailboxGetMethod.scala       |  8 ++--
 .../james/jmap/method/MailboxQueryMethod.scala     |  6 +--
 .../jmap/method/MailboxSetCreatePerformer.scala    |  8 +---
 .../james/jmap/method/MailboxSetMethod.scala       |  6 +--
 .../jmap/method/PushSubscriptionGetMethod.scala    |  7 ++-
 .../PushSubscriptionSetCreatePerformer.scala       |  8 +---
 .../jmap/method/PushSubscriptionSetMethod.scala    |  6 +--
 .../james/jmap/method/QuotaChangesMethod.scala     |  6 +--
 .../apache/james/jmap/method/QuotaGetMethod.scala  |  5 +-
 .../james/jmap/method/QuotaQueryMethod.scala       |  8 ++--
 .../james/jmap/method/ThreadChangesMethod.scala    |  6 +--
 .../apache/james/jmap/method/ThreadGetMethod.scala |  7 ++-
 .../jmap/method/VacationResponseGetMethod.scala    |  6 +--
 .../jmap/method/VacationResponseSetMethod.scala    |  6 +--
 .../org/apache/james/jmap/method/package.scala     | 48 +++++++++++++++++++
 43 files changed, 217 insertions(+), 250 deletions(-)

diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/DelegateSetContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/DelegateSetContract.scala
index ac675eaa67..5eb3092b3a 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/DelegateSetContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/DelegateSetContract.scala
@@ -226,7 +226,7 @@ trait DelegateSetContract {
            |                           "notCreated": {
            |                                   "4f29": {
            |                                           "type": 
"invalidArguments",
-           |                                           "description": "Missing 
'/username' property in Delegate object"
+           |                                           "description": "Missing 
'/username' property"
            |                                   }
            |                           }
            |                   },
@@ -280,7 +280,7 @@ trait DelegateSetContract {
            |                           "notCreated": {
            |                                   "4f29": {
            |                                           "type": 
"invalidArguments",
-           |                                           "description": 
"'/username' property in Delegate object is not valid: username needs to be 
represented by a JsString"
+           |                                           "description": 
"'/username' property is not valid: username needs to be represented by a 
JsString"
            |                                   }
            |                           }
            |                   },
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index 15613ffd86..c06761c68e 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -1951,7 +1951,7 @@ trait EmailQueryMethodContract {
                       |            "error",
                       |            {
                       |                "type": "invalidArguments",
-                      |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.header\\",\\"messages\\":[\\"header 
filter needs to be an array of one or two strings\\"]}]}"
+                      |                "description": "'/filter/header' 
property is not valid: header filter needs to be an array of one or two strings"
                       |            },
                       |            "c1"
                       |        ]
@@ -3454,7 +3454,7 @@ trait EmailQueryMethodContract {
         .isEqualTo("""
          {
             "type": "invalidArguments",
-            "description": 
"{\"errors\":[{\"path\":\"obj.sort[0].property\",\"messages\":[\"error.path.missing\"]}]}"
+            "description": "Missing '/sort(0)/property' property"
           }
          """)
     }
@@ -3551,7 +3551,7 @@ trait EmailQueryMethodContract {
         .isEqualTo("""
          {
             "type": "invalidArguments",
-            "description": 
"{\"errors\":[{\"path\":\"obj.sort[0].property\",\"messages\":[\"'unsupported' 
is not a supported sort property\"]}]}"
+            "description": "'/sort(0)/property' property is not valid: 
'unsupported' is not a supported sort property"
           }
          """)
     }
@@ -3994,7 +3994,7 @@ trait EmailQueryMethodContract {
              |            "error",
              |            {
              |                "type": "invalidArguments",
-             |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.maxSize\\",\\"messages\\":[\\"Predicate
 (-1 < 0) did not fail.\\"]}]}"
+             |                "description": "'/filter/maxSize' property is 
not valid: Predicate (-1 < 0) did not fail."
              |            },
              |            "c1"
              |        ]
@@ -4043,7 +4043,7 @@ trait EmailQueryMethodContract {
              |            "error",
              |            {
              |                "type": "invalidArguments",
-             |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.minSize\\",\\"messages\\":[\\"Predicate
 (-1 < 0) did not fail.\\"]}]}"
+             |                "description": "'/filter/minSize' property is 
not valid: Predicate (-1 < 0) did not fail."
              |            },
              |            "c1"
              |        ]
@@ -6936,7 +6936,7 @@ trait EmailQueryMethodContract {
                     |            "error",
                     |            {
                     |                "type": "invalidArguments",
-                    |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter\\",\\"messages\\":[\\"Expecting 
filterOperator to contain only operator and conditions\\"]}]}"
+                    |                "description": "'/filter' property is not 
valid: Expecting filterOperator to contain only operator and conditions"
                     |            },
                     |            "c1"
                     |        ]
@@ -6982,7 +6982,7 @@ trait EmailQueryMethodContract {
                     |            "error",
                     |            {
                     |                "type": "invalidArguments",
-                    |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter\\",\\"messages\\":[\\"Expecting 
filterOperator to contain only operator and conditions\\"]}]}"
+                    |                "description": "'/filter' property is not 
valid: Expecting filterOperator to contain only operator and conditions"
                     |            },
                     |            "c1"
                     |        ]
@@ -7404,7 +7404,7 @@ trait EmailQueryMethodContract {
         .isEqualTo(
         """{
           |  "type": "invalidArguments",
-          |  "description": 
"{\"errors\":[{\"path\":\"obj.filter\",\"messages\":[\"These 
'[unsupported_option, role]' was unsupported filter options\"]}]}"
+          |  "description": "'/filter' property is not valid: These 
'[unsupported_option, role]' was unsupported filter options"
           |}
           |""".stripMargin)
   }
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/IdentitySetContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/IdentitySetContract.scala
index 55f4c8db35..f83d45a1c9 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/IdentitySetContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/IdentitySetContract.scala
@@ -663,7 +663,7 @@ trait IdentitySetContract {
            |   "notCreated": {
            |           "4f29": {
            |                   "type": "invalidArguments",
-           |                   "description": "Missing '/email' property in 
Identity object"
+           |                   "description": "Missing '/email' property"
            |           }
            |   }
            |}""".stripMargin)
@@ -721,7 +721,7 @@ trait IdentitySetContract {
            |   "notCreated": {
            |           "4f29": {
            |                   "type": "invalidArguments",
-           |                   "description": "'/email' property in Identity 
object is not valid: mail address needs to be represented with a JsString"
+           |                   "description": "'/email' property is not valid: 
mail address needs to be represented with a JsString"
            |           }
            |   }
            |}""".stripMargin)
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
index 25ad0b2bd8..2b77c2db88 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
@@ -340,7 +340,7 @@ trait MailboxQueryMethodContract {
            |            "error",
            |            {
            |                "type": "invalidArguments",
-           |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.role\\",\\"messages\\":[\\"Invalid is 
not a valid role\\"]}]}"
+           |                "description": "'/filter/role' property is not 
valid: Invalid is not a valid role"
            |            },
            |            "c1"
            |        ]
@@ -385,7 +385,7 @@ trait MailboxQueryMethodContract {
            |            "error",
            |            {
            |                "type": "invalidArguments",
-           |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.role\\",\\"messages\\":[\\"Expecting 
a JsString to be representing a role\\"]}]}"
+           |                "description": "'/filter/role' property is not 
valid: Expecting a JsString to be representing a role"
            |            },
            |            "c1"
            |        ]
@@ -431,7 +431,7 @@ trait MailboxQueryMethodContract {
            |            "error",
            |            {
            |                "type": "invalidArguments",
-           |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.role\\",\\"messages\\":[\\"error.path.missing\\"]}]}"
+           |                "description": "Missing '/filter/role' property"
            |            },
            |            "c1"
            |        ]
@@ -479,7 +479,7 @@ trait MailboxQueryMethodContract {
            |            "error",
            |            {
            |                "type": "invalidArguments",
-           |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter\\",\\"messages\\":[\\"These 
'[unsupported_option]' was unsupported filter options\\"]}]}"
+           |                "description": "'/filter' property is not valid: 
These '[unsupported_option]' was unsupported filter options"
            |            },
            |            "c1"
            |        ]
@@ -523,7 +523,7 @@ trait MailboxQueryMethodContract {
            |            "error",
            |            {
            |                "type": "invalidArguments",
-           |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter\\",\\"messages\\":[\\"error.path.missing\\"]}]}"
+           |                "description": "Missing '/filter' property"
            |            },
            |            "c1"
            |        ]
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index ba755bedfc..261eaab4ed 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -786,7 +786,7 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C42": {
          |          "type": "invalidArguments",
-         |          "description": "Missing '/name' property in mailbox object"
+         |          "description": "Missing '/name' property"
          |        }
          |      }
          |    },
@@ -843,7 +843,7 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C42": {
          |          "type": "invalidArguments",
-         |          "description": "'/name' property in mailbox object is not 
valid: Predicate isEmpty() did not fail."
+         |          "description": "'/name' property is not valid: Predicate 
isEmpty() did not fail."
          |        }
          |      }
          |    },
@@ -1077,7 +1077,7 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C42": {
          |          "type": "invalidArguments",
-         |          "description": "'/parentId' property in mailbox object is 
not valid: mailboxId does not match Id constraints: Predicate failed: '' 
contains some invalid characters. Should be [#a-zA-Z0-9-_] and no longer than 
255 chars."
+         |          "description": "'/parentId' property is not valid: 
mailboxId does not match Id constraints: Predicate failed: '' contains some 
invalid characters. Should be [#a-zA-Z0-9-_] and no longer than 255 chars."
          |        }
          |      }
          |    },
@@ -1471,7 +1471,7 @@ trait MailboxSetMethodContract {
          |                     "notCreated": {
          |                             "C42": {
          |                                     "type": "invalidArguments",
-         |                                     "description": 
"'/rights/an...@domain.tld(0)' property in mailbox object is not valid: Rights 
must have size 1"
+         |                                     "description": 
"'/rights/an...@domain.tld(0)' property is not valid: Rights must have size 1"
          |                             }
          |                     }
          |             }, "c1"]
@@ -1529,7 +1529,7 @@ trait MailboxSetMethodContract {
          |                     "notCreated": {
          |                             "C42": {
          |                                     "type": "invalidArguments",
-         |                                     "description": 
"'/rights/an...@domain.tld(0)' property in mailbox object is not valid: Unknown 
right 'z'"
+         |                                     "description": 
"'/rights/an...@domain.tld(0)' property is not valid: Unknown right 'z'"
          |                             }
          |                     }
          |             }, "c1"]
@@ -1808,7 +1808,7 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C43": {
          |          "type": "invalidArguments",
-         |          "description": "Missing '/name' property in mailbox object"
+         |          "description": "Missing '/name' property"
          |        }
          |      }
          |    },
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/PushSubscriptionSetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/PushSubscriptionSetMethodContract.scala
index ac5a01da6f..773186c0da 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/PushSubscriptionSetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/PushSubscriptionSetMethodContract.scala
@@ -477,7 +477,7 @@ trait PushSubscriptionSetMethodContract {
            |                "notCreated": {
            |                    "4f29": {
            |                        "type": "invalidArguments",
-           |                        "description": "Missing '/types' property 
in PushSubscription object"
+           |                        "description": "Missing '/types' property"
            |                    }
            |                }
            |            },
@@ -937,7 +937,7 @@ trait PushSubscriptionSetMethodContract {
            |                "notCreated": {
            |                    "4f29": {
            |                        "type": "invalidArguments",
-           |                        "description": "'/url' property in 
PushSubscription object is not valid"
+           |                        "description": "'/url' property is not 
valid"
            |                    }
            |                }
            |            },
@@ -1164,7 +1164,7 @@ trait PushSubscriptionSetMethodContract {
            |                "notCreated": {
            |                    "4f29": {
            |                        "type": "invalidArguments",
-           |                        "description": "'/types(0)' property in 
PushSubscription object is not valid: Unknown typeName invalid"
+           |                        "description": "'/types(0)' property is 
not valid: Unknown typeName invalid"
            |                    }
            |                }
            |            },
@@ -1462,7 +1462,7 @@ trait PushSubscriptionSetMethodContract {
            |                "notCreated": {
            |                    "4f29": {
            |                        "type": "invalidArguments",
-           |                        "description": "'/types(0)' property in 
PushSubscription object is not valid: Unknown typeName invalid"
+           |                        "description": "'/types(0)' property is 
not valid: Unknown typeName invalid"
            |                    }
            |                }
            |            },
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaChangesMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaChangesMethodContract.scala
index 86029b2262..dacb2fa693 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaChangesMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaChangesMethodContract.scala
@@ -341,7 +341,7 @@ trait QuotaChangesMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.sinceState\\",\\"messages\\":[\\"error.path.missing\\"]}]}"
+         |                "description": "Missing '/sinceState' property"
          |            },
          |            "c1"
          |        ]
@@ -382,7 +382,7 @@ trait QuotaChangesMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.sinceState\\",\\"messages\\":[\\"error.expected.uuid\\"]}]}"
+         |                "description": "'/sinceState' property is not valid: 
error.expected.uuid"
          |            },
          |            "c1"
          |        ]
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaQueryMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaQueryMethodContract.scala
index 19b66125a0..8279966465 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaQueryMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/QuotaQueryMethodContract.scala
@@ -254,7 +254,7 @@ trait QuotaQueryMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.resourceTypes[0]\\",\\"messages\\":[\\"Unexpected
 value invalid, only 'count' and 'octets' are managed\\"]}]}"
+         |                "description": "'/filter/resourceTypes(0)' property 
is not valid: Unexpected value invalid, only 'count' and 'octets' are managed"
          |            },
          |            "c1"
          |        ]
@@ -357,7 +357,7 @@ trait QuotaQueryMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.dataTypes[0]\\",\\"messages\\":[\\"Unexpected
 value invalid, only 'Mail' are managed\\"]}]}"
+         |                "description": "'/filter/dataTypes(0)' property is 
not valid: Unexpected value invalid, only 'Mail' are managed"
          |            },
          |            "c1"
          |        ]
@@ -460,7 +460,7 @@ trait QuotaQueryMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter.scope[0]\\",\\"messages\\":[\\"Unexpected
 value invalidScope, only \'account\' is managed\\"]}]}"
+         |                "description": "'/filter/scope(0)' property is not 
valid: Unexpected value invalidScope, only 'account' is managed"
          |            },
          |            "c1"
          |        ]
@@ -724,7 +724,7 @@ trait QuotaQueryMethodContract {
          |            "error",
          |            {
          |                "type": "invalidArguments",
-         |                "description": 
"{\\"errors\\":[{\\"path\\":\\"obj.filter\\",\\"messages\\":[\\"These 
'[filterName1]' was unsupported filter options\\"]}]}"
+         |                "description": "'/filter' property is not valid: 
These '[filterName1]' was unsupported filter options"
          |            },
          |            "c1"
          |        ]
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/delegation/DelegateSet.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/delegation/DelegateSet.scala
index 9e8cacf0eb..899ba8cbea 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/delegation/DelegateSet.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/delegation/DelegateSet.scala
@@ -21,9 +21,8 @@ package org.apache.james.jmap.delegation
 
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.Id.Id
-import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{AccountId, Id, SetError, UuidState}
-import org.apache.james.jmap.method.WithAccountId
+import org.apache.james.jmap.method.{WithAccountId, standardError}
 import play.api.libs.json.{JsObject, JsPath, JsonValidationError}
 
 case class DelegateCreationId(id: Id) {
@@ -52,14 +51,6 @@ case class DelegateSetParseException(setError: SetError) 
extends IllegalArgument
 case class ForbiddenAccountManagementException() extends RuntimeException()
 
 object DelegateSetParseException {
-  def from(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): DelegateSetParseException = {
-    val setError: SetError = errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in Delegate 
object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) =>
-        SetError.invalidArguments(SetErrorDescription(s"Missing '$path' 
property in Delegate object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in Delegate 
object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
-    DelegateSetParseException(setError)
-  }
+  def from(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): DelegateSetParseException =
+    DelegateSetParseException(standardError(errors))
 }
\ No newline at end of file
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/IdentitySet.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/IdentitySet.scala
index 356e82942e..6b44f773e7 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/IdentitySet.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/IdentitySet.scala
@@ -24,7 +24,7 @@ import org.apache.james.jmap.core.Id.Id
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{AccountId, Properties, SetError, UuidState}
 import 
org.apache.james.jmap.method.IdentitySetUpdatePerformer.IdentitySetUpdateResponse
-import org.apache.james.jmap.method.WithAccountId
+import org.apache.james.jmap.method.{WithAccountId, standardError}
 import play.api.libs.json.{JsObject, JsPath, JsonValidationError}
 
 object IdentitySet {
@@ -76,14 +76,5 @@ case class IdentitySetResponse(accountId: AccountId,
 case class IdentitySetParseException(setError: SetError) extends 
IllegalArgumentException
 
 object IdentitySetParseException {
-  def from(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): IdentitySetParseException = {
-    val setError: SetError = errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in Identity 
object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) =>
-        SetError.invalidArguments(SetErrorDescription(s"Missing '$path' 
property in Identity object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in Identity 
object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
-    IdentitySetParseException(setError)
-  }
+  def from(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): IdentitySetParseException = 
IdentitySetParseException(standardError(errors))
 }
\ No newline at end of file
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MDNSend.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MDNSend.scala
index f0390ab4bd..c69effc27f 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MDNSend.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MDNSend.scala
@@ -19,16 +19,16 @@
 
 package org.apache.james.jmap.mail
 
-import java.util.UUID
-
 import cats.implicits.toTraverseOps
 import org.apache.james.jmap.core.Id.Id
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{AccountId, Id, Properties, SetError}
-import org.apache.james.jmap.method.WithAccountId
+import org.apache.james.jmap.method.{WithAccountId, standardError}
 import org.apache.james.mailbox.model.MessageId
 import play.api.libs.json.{JsObject, JsPath, JsonValidationError}
 
+import java.util.UUID
+
 object MDNSend {
   val MDN_ALREADY_SENT_FLAG: String = "$mdnsent"
 }
@@ -42,15 +42,8 @@ case class MDNSendCreationId(id: Id)
 case class MDNId(value: Id)
 
 object MDNSendRequestInvalidException {
-  def parse(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): MDNSendRequestInvalidException = {
-    val setError: SetError = errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in MDNSend 
object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => 
SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in 
MDNSend object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in MDNSend 
object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
-    MDNSendRequestInvalidException(setError)
-  }
+  def parse(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): MDNSendRequestInvalidException =
+    MDNSendRequestInvalidException(standardError(errors))
 }
 
 case class MDNSendRequestInvalidException(error: SetError) extends Exception
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateGetMethod.scala
index b56fe00959..16cc71fab8 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateGetMethod.scala
@@ -20,14 +20,12 @@
 package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_DELEGATION, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, Properties, 
SessionTranslator}
-import org.apache.james.jmap.delegation.{Delegate, DelegateGet, 
DelegateGetRequest, DelegateGetResult, ForbiddenAccountManagementException}
-import org.apache.james.jmap.delegation.{Delegate, DelegateGet, 
DelegateGetRequest, DelegateGetResult, DelegationId}
-import org.apache.james.jmap.json.{DelegationSerializer, ResponseSerializer}
+import org.apache.james.jmap.delegation.{Delegate, DelegateGet, 
DelegateGetRequest, DelegateGetResult, DelegationId, 
ForbiddenAccountManagementException}
+import org.apache.james.jmap.json.DelegationSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
@@ -36,6 +34,7 @@ import org.reactivestreams.Publisher
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
 
+import javax.inject.Inject
 import scala.jdk.OptionConverters._
 
 
@@ -48,8 +47,7 @@ class DelegateGetMethod @Inject()(val metricFactory: 
MetricFactory,
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, JAMES_DELEGATION)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, DelegateGetRequest] =
-    
DelegationSerializer.deserializeDelegateGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
DelegationSerializer.deserializeDelegateGetRequest(invocation.arguments.value).asEitherRequest
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
DelegateGetRequest): Publisher[InvocationWithContext] = {
     val requestedProperties: Properties = 
request.properties.getOrElse(DelegateGet.allProperties)
     (requestedProperties -- DelegateGet.allProperties match {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetMethod.scala
index a49692cdc2..2b842908a0 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetMethod.scala
@@ -24,12 +24,11 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JA
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, 
SessionTranslator, UuidState}
 import org.apache.james.jmap.delegation.{DelegateSetRequest, 
DelegateSetResponse, ForbiddenAccountManagementException}
-import org.apache.james.jmap.json.{DelegationSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.DelegationSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.MailboxSession.isPrimaryAccount
 import org.apache.james.metrics.api.MetricFactory
-import play.api.libs.json.{JsError, JsSuccess}
 import reactor.core.scala.publisher.SMono
 
 import javax.inject.Inject
@@ -43,10 +42,7 @@ class DelegateSetMethod @Inject()(createPerformer: 
DelegateSetCreatePerformer,
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, JAMES_DELEGATION)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, DelegateSetRequest] =
-    
DelegationSerializer.deserializeDelegateSetRequest(invocation.arguments.value) 
match {
-      case JsSuccess(delegateSetRequest, _) => Right(delegateSetRequest)
-      case errors: JsError => Left(new 
IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
-    }
+    
DelegationSerializer.deserializeDelegateSetRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
DelegateSetRequest): SMono[InvocationWithContext] =
     if (isPrimaryAccount(mailboxSession)) {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountGetMethod.scala
index 96330d7eae..2e43eb916b 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountGetMethod.scala
@@ -20,14 +20,12 @@
 package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_DELEGATION, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, Properties, 
SessionTranslator}
-import org.apache.james.jmap.delegation.{Delegate, DelegatedAccountGet, 
DelegatedAccountGetRequest, DelegatedAccountGetResult, 
ForbiddenAccountManagementException}
-import org.apache.james.jmap.delegation.{Delegate, DelegatedAccountGet, 
DelegatedAccountGetRequest, DelegatedAccountGetResult, DelegationId}
-import org.apache.james.jmap.json.{DelegationSerializer, ResponseSerializer}
+import org.apache.james.jmap.delegation.{Delegate, DelegatedAccountGet, 
DelegatedAccountGetRequest, DelegatedAccountGetResult, DelegationId, 
ForbiddenAccountManagementException}
+import org.apache.james.jmap.json.DelegationSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
@@ -36,6 +34,7 @@ import org.reactivestreams.Publisher
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
 
+import javax.inject.Inject
 import scala.jdk.OptionConverters._
 
 class DelegatedAccountGetMethod @Inject()(val metricFactory: MetricFactory,
@@ -47,8 +46,7 @@ class DelegatedAccountGetMethod @Inject()(val metricFactory: 
MetricFactory,
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, JAMES_DELEGATION)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, DelegatedAccountGetRequest] =
-    
DelegationSerializer.deserializeDelegatedAccountGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
DelegationSerializer.deserializeDelegatedAccountGetRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
DelegatedAccountGetRequest): Publisher[InvocationWithContext] = {
     val requestedProperties: Properties = 
request.properties.getOrElse(DelegatedAccountGet.allProperties)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountSetMethod.scala
index 3f9318fc24..c8fbfe44bd 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountSetMethod.scala
@@ -20,19 +20,19 @@
 package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_DELEGATION, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{Invocation, SessionTranslator, UuidState}
 import org.apache.james.jmap.delegation.{DelegatedAccountSetRequest, 
DelegatedAccountSetResponse, ForbiddenAccountManagementException}
-import org.apache.james.jmap.json.{DelegationSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.DelegationSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.MailboxSession.isPrimaryAccount
 import org.apache.james.metrics.api.MetricFactory
-import play.api.libs.json.{JsError, JsSuccess}
 import reactor.core.scala.publisher.SMono
 
+import javax.inject.Inject
+
 class DelegatedAccountSetMethod @Inject()(deletePerformer: 
DelegatedAccountDeletePerformer,
                                           val metricFactory: MetricFactory,
                                           val sessionSupplier: SessionSupplier,
@@ -41,10 +41,7 @@ class DelegatedAccountSetMethod @Inject()(deletePerformer: 
DelegatedAccountDelet
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, JAMES_DELEGATION)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, DelegatedAccountSetRequest] =
-    
DelegationSerializer.deserializeDelegatedAccountSetRequest(invocation.arguments.value)
 match {
-      case JsSuccess(delegatedAccountSetRequest, _) => 
Right(delegatedAccountSetRequest)
-      case errors: JsError => Left(new 
IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
-    }
+    
DelegationSerializer.deserializeDelegatedAccountSetRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
DelegatedAccountSetRequest): SMono[InvocationWithContext] =
     if (isPrimaryAccount(mailboxSession)) {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailChangesMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailChangesMethod.scala
index e5a0e91060..4b9913bbeb 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailChangesMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailChangesMethod.scala
@@ -25,14 +25,14 @@ import org.apache.james.jmap.api.model.{AccountId => 
JavaAccountId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_SHARES, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, SessionTranslator, 
UuidState}
-import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailGetSerializer
 import org.apache.james.jmap.mail.{EmailChangesRequest, EmailChangesResponse, 
HasMoreChanges}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
-import javax.inject.Inject
 
+import javax.inject.Inject
 import scala.jdk.CollectionConverters._
 import scala.jdk.OptionConverters._
 
@@ -74,6 +74,5 @@ class EmailChangesMethod @Inject()(val metricFactory: 
MetricFactory,
     }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, EmailChangesRequest] =
-    
EmailGetSerializer.deserializeEmailChangesRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
EmailGetSerializer.deserializeEmailChangesRequest(invocation.arguments.value).asEitherRequest
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index ec853b2996..e5d420c09c 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -18,17 +18,14 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
-import java.time.ZoneId
-
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.jmap.api.change.{EmailChangeRepository, State => 
JavaState}
 import org.apache.james.jmap.api.model.{AccountId => JavaAccountId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_SHARES, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.UuidState.INSTANCE
 import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, 
SessionTranslator, UuidState}
-import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailGetSerializer
 import org.apache.james.jmap.mail.{Email, EmailGetRequest, EmailGetResponse, 
EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, UnparsedEmailId}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
@@ -38,6 +35,9 @@ import org.slf4j.{Logger, LoggerFactory}
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
 
+import java.time.ZoneId
+import javax.inject.Inject
+
 object EmailGetResults {
   private val logger: Logger = 
LoggerFactory.getLogger(classOf[EmailGetResults])
 
@@ -92,8 +92,7 @@ class EmailGetMethod @Inject() (readerFactory: 
EmailViewReaderFactory,
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, EmailGetRequest] =
-    EmailGetSerializer.deserializeEmailGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
EmailGetSerializer.deserializeEmailGetRequest(invocation.arguments.value).asEitherRequest
 
   private def computeResponseInvocation(capabilities: 
Set[CapabilityIdentifier], request: EmailGetRequest, invocation: Invocation, 
mailboxSession: MailboxSession): SMono[Invocation] =
     Email.validateProperties(request.properties)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
index 799cf92838..d2da447b39 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
@@ -27,7 +27,7 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JA
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, 
SessionTranslator, SetError, UuidState}
-import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailSetSerializer
 import org.apache.james.jmap.mail.{BlobId, EmailCreationId, 
EmailCreationResponse, EmailImport, EmailImportRequest, EmailImportResponse, 
ThreadId, ValidatedEmailImport}
 import org.apache.james.jmap.method.EmailImportMethod.{ImportFailure, 
ImportResult, ImportResults, ImportSuccess, ImportWithBlob}
 import org.apache.james.jmap.routes.{Blob, BlobNotFoundException, 
BlobResolvers, ProcessingContext, SessionSupplier}
@@ -42,10 +42,9 @@ import org.apache.james.mime4j.stream.MimeConfig
 import org.apache.james.util.ReactorUtils
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.{SFlux, SMono}
-import java.util.Date
 
+import java.util.Date
 import javax.inject.Inject
-
 import scala.util.{Try, Using}
 
 object EmailImportMethod {
@@ -91,8 +90,7 @@ class EmailImportMethod @Inject() (val metricFactory: 
MetricFactory,
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, JMAP_MAIL)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, EmailImportRequest] =
-    serializer.deserializeEmailImportRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
serializer.deserializeEmailImportRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
EmailImportRequest): Publisher[InvocationWithContext] =
     for {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailParseMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailParseMethod.scala
index 6d6d65e2ce..2e89d3cc79 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailParseMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailParseMethod.scala
@@ -19,24 +19,23 @@
 
 package org.apache.james.jmap.method
 
-import java.io.InputStream
-
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.jmap.api.model.Preview
 import org.apache.james.jmap.api.model.Size.Size
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation._
 import org.apache.james.jmap.core.{Invocation, SessionTranslator}
-import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailGetSerializer
 import org.apache.james.jmap.mail.{BlobId, BlobUnParsableException, Email, 
EmailBody, EmailBodyMetadata, EmailBodyPart, EmailFullViewFactory, 
EmailHeaders, EmailParseMetadata, EmailParseRequest, EmailParseResponse, 
EmailParseResults, EmailParseView, HasAttachment}
 import org.apache.james.jmap.routes.{BlobNotFoundException, BlobResolvers, 
SessionSupplier}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.util.html.HtmlTextExtractor
-import play.api.libs.json.{JsError, JsObject, JsSuccess, Json}
+import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
 
+import java.io.InputStream
+import javax.inject.Inject
 import scala.util.Try
 
 class EmailParseMethod @Inject()(val blobResolvers: BlobResolvers,
@@ -57,10 +56,7 @@ class EmailParseMethod @Inject()(val blobResolvers: 
BlobResolvers,
       .map(InvocationWithContext(_, invocation.processingContext))
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, EmailParseRequest] =
-    
EmailGetSerializer.deserializeEmailParseRequest(invocation.arguments.value) 
match {
-      case JsSuccess(parseRequest, _) => Right(parseRequest)
-      case errors: JsError => Left(new 
IllegalArgumentException(Json.stringify(ResponseSerializer.serialize(errors))))
-    }
+    
EmailGetSerializer.deserializeEmailParseRequest(invocation.arguments.value).asEitherRequest
 
   def computeResponseInvocation(request: EmailParseRequest,
                                 invocation: Invocation,
@@ -104,25 +100,25 @@ class EmailParseMethod @Inject()(val blobResolvers: 
BlobResolvers,
       }
 
   private def parse(request: EmailParseRequest, blobId: BlobId, blobContent: 
=> InputStream, size: Size): SMono[EmailParseResults] = {
-     val result: SMono[EmailParseView] = for {
-       mime4JMessage <- 
SMono.fromTry(Email.parseStreamAsMime4JMessage(blobContent))
-       bodyStructure <- SMono.fromTry(EmailBodyPart.of(request.bodyProperties, 
zoneIdProvider.get(), blobId, mime4JMessage))
-       bodyValues <- 
SMono.fromTry(EmailFullViewFactory.extractBodyValuesForParse(htmlTextExtractor)(bodyStructure,
 request))
-       preview <- 
SMono.fromTry(Try(previewFactory.fromMime4JMessage(mime4JMessage)))
-     } yield {
-       EmailParseView(
-         metadata = EmailParseMetadata(blobId = blobId, size = size),
-         header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
-         specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), 
mime4JMessage.getHeader),
-         bodyMetadata = EmailBodyMetadata(hasAttachment = 
HasAttachment(bodyStructure.attachments.nonEmpty),
-           preview = preview),
-         body = EmailBody(
-           bodyStructure = bodyStructure,
-           textBody = bodyStructure.textBody,
-           htmlBody = bodyStructure.htmlBody,
-           attachments = bodyStructure.attachments,
-           bodyValues = bodyValues))
-     }
-     result.map(EmailParseResults.parsed(blobId, _))
+    val result: SMono[EmailParseView] = for {
+      mime4JMessage <- 
SMono.fromTry(Email.parseStreamAsMime4JMessage(blobContent))
+      bodyStructure <- SMono.fromTry(EmailBodyPart.of(request.bodyProperties, 
zoneIdProvider.get(), blobId, mime4JMessage))
+      bodyValues <- 
SMono.fromTry(EmailFullViewFactory.extractBodyValuesForParse(htmlTextExtractor)(bodyStructure,
 request))
+      preview <- 
SMono.fromTry(Try(previewFactory.fromMime4JMessage(mime4JMessage)))
+    } yield {
+      EmailParseView(
+        metadata = EmailParseMetadata(blobId = blobId, size = size),
+        header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
+        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), 
mime4JMessage.getHeader),
+        bodyMetadata = EmailBodyMetadata(hasAttachment = 
HasAttachment(bodyStructure.attachments.nonEmpty),
+          preview = preview),
+        body = EmailBody(
+          bodyStructure = bodyStructure,
+          textBody = bodyStructure.textBody,
+          htmlBody = bodyStructure.htmlBody,
+          attachments = bodyStructure.attachments,
+          bodyValues = bodyValues))
+    }
+    result.map(EmailParseResults.parsed(blobId, _))
   }
 }
\ No newline at end of file
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index f1902846ba..d31424ba19 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -18,12 +18,8 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
-import java.time.ZonedDateTime
-
 import cats.implicits._
 import eu.timepit.refined.auto._
-import javax.inject.Inject
-import javax.mail.Flags.Flag.DELETED
 import org.apache.james.jmap.JMAPConfiguration
 import org.apache.james.jmap.api.projections.EmailQueryView
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL}
@@ -31,7 +27,7 @@ import org.apache.james.jmap.core.Invocation.{Arguments, 
MethodName}
 import org.apache.james.jmap.core.Limit.Limit
 import org.apache.james.jmap.core.Position.Position
 import org.apache.james.jmap.core.{CanCalculateChanges, Invocation, Limit, 
Position, QueryState, SessionTranslator}
-import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailQuerySerializer
 import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, 
EmailQueryResponse, FilterCondition, UnsupportedRequestParameterException}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.search.MailboxFilter
@@ -44,6 +40,9 @@ import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.util.streams.{Limit => JavaLimit}
 import reactor.core.scala.publisher.{SFlux, SMono}
 
+import java.time.ZonedDateTime
+import javax.inject.Inject
+import javax.mail.Flags.Flag.DELETED
 import scala.jdk.CollectionConverters._
 
 class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
@@ -81,7 +80,7 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, EmailQueryRequest] =
     serializer.deserializeEmailQueryRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+      .asEitherRequest
       .flatMap(validateRequestParameters)
 
   private def validateRequestParameters(request: EmailQueryRequest): 
Either[Exception, EmailQueryRequest] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index 376fda8800..4bdef7d354 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -24,13 +24,14 @@ import org.apache.james.jmap.api.model.{AccountId => 
JavaAccountId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_SHARES, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, 
SessionTranslator, UuidState}
-import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.EmailSetSerializer
 import org.apache.james.jmap.mail.{EmailSetRequest, EmailSetResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 case class MessageNotFoundException(messageId: MessageId) extends Exception
@@ -77,8 +78,7 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, EmailSetRequest] =
-    serializer.deserialize(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    serializer.deserialize(invocation.arguments.value).asEitherRequest
 
   private def retrieveState(capabilities: Set[CapabilityIdentifier], 
mailboxSession: MailboxSession): SMono[UuidState] =
     if (capabilities.contains(JAMES_SHARES)) {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index cf4647e476..7bbbf6597a 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -19,23 +19,16 @@
 
 package org.apache.james.jmap.method
 
-import java.io.InputStream
-
 import cats.implicits._
 import eu.timepit.refined.auto._
 import eu.timepit.refined.refineV
-import javax.annotation.PreDestroy
-import javax.inject.Inject
-import javax.mail.Address
-import javax.mail.Message.RecipientType
-import javax.mail.internet.{InternetAddress, MimeMessage}
 import org.apache.james.core.{MailAddress, Username}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
EMAIL_SUBMISSION, JMAP_CORE}
 import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType}
 import org.apache.james.jmap.core.{ClientId, Invocation, Properties, ServerId, 
SessionTranslator, SetError, UuidState}
-import org.apache.james.jmap.json.{EmailSubmissionSetSerializer, 
ResponseSerializer}
+import org.apache.james.jmap.json.EmailSubmissionSetSerializer
 import org.apache.james.jmap.mail.{EmailSubmissionAddress, 
EmailSubmissionCreationId, EmailSubmissionCreationRequest, 
EmailSubmissionCreationResponse, EmailSubmissionId, EmailSubmissionSetRequest, 
EmailSubmissionSetResponse, Envelope}
 import org.apache.james.jmap.method.EmailSubmissionSetMethod.{CreationFailure, 
CreationResult, CreationResults, CreationSuccess, LOGGER, 
MAIL_METADATA_USERNAME_ATTRIBUTE}
 import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
@@ -47,7 +40,6 @@ import org.apache.james.queue.api.MailQueueFactory.SPOOL
 import org.apache.james.queue.api.{MailQueue, MailQueueFactory}
 import org.apache.james.rrt.api.CanSendFrom
 import org.apache.james.server.core.{MailImpl, MimeMessageSource, 
MimeMessageWrapper}
-import org.apache.james.util.ReactorUtils
 import org.apache.mailet.{Attribute, AttributeName, AttributeValue}
 import org.slf4j.{Logger, LoggerFactory}
 import play.api.libs.json._
@@ -55,6 +47,12 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 import reactor.util.concurrent.Queues
 
+import java.io.InputStream
+import javax.annotation.PreDestroy
+import javax.inject.Inject
+import javax.mail.Address
+import javax.mail.Message.RecipientType
+import javax.mail.internet.{InternetAddress, MimeMessage}
 import scala.jdk.CollectionConverters._
 import scala.util.{Failure, Success, Try}
 
@@ -190,13 +188,10 @@ class EmailSubmissionSetMethod @Inject()(serializer: 
EmailSubmissionSetSerialize
         SFlux.concat(SMono.just(explicitInvocation), emailSetCall)
       })
 
-  override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, EmailSubmissionSetRequest] = {
-    val maybeRequestRequest = 
serializer.deserializeEmailSubmissionSetRequest(invocation.arguments.value) 
match {
-      case JsSuccess(emailSubmissionSetRequest, _) => 
Right(emailSubmissionSetRequest)
-      case errors: JsError => Left(new 
IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
-    }
-    maybeRequestRequest.flatMap(_.validate)
-  }
+  override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, EmailSubmissionSetRequest] =
+    serializer.deserializeEmailSubmissionSetRequest(invocation.arguments.value)
+      .asEitherRequest
+      .flatMap(_.validate)
 
   private def create(request: EmailSubmissionSetRequest,
                      session: MailboxSession,
@@ -241,12 +236,7 @@ class EmailSubmissionSetMethod @Inject()(serializer: 
EmailSubmissionSetSerialize
       })
 
   private def emailSubmissionSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError =
-    errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in 
EmailSubmission object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => 
SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in 
EmailSubmission object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in 
EmailSubmission object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
+    standardError(errors)
 
   private def sendEmail(mailboxSession: MailboxSession,
                         request: EmailSubmissionCreationRequest): 
SMono[(EmailSubmissionCreationResponse, MessageId)] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityChangesMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityChangesMethod.scala
index b4207de0b6..3a7a44ed27 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityChangesMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityChangesMethod.scala
@@ -20,11 +20,10 @@
 package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
-import javax.inject.Inject
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
EMAIL_SUBMISSION}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, SessionTranslator, 
UuidState}
-import org.apache.james.jmap.json.{IdentitySerializer, ResponseSerializer}
+import org.apache.james.jmap.json.IdentitySerializer
 import org.apache.james.jmap.mail.{HasMoreChanges, IdentityChangesRequest, 
IdentityChangesResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
@@ -32,6 +31,8 @@ import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
 
+import javax.inject.Inject
+
 class IdentityChangesMethod @Inject()(val metricFactory: MetricFactory,
                                       val sessionSupplier: SessionSupplier,
                                       val sessionTranslator: 
SessionTranslator) extends MethodRequiringAccountId[IdentityChangesRequest] {
@@ -61,6 +62,5 @@ class IdentityChangesMethod @Inject()(val metricFactory: 
MetricFactory,
     }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, IdentityChangesRequest] =
-    IdentitySerializer.deserializeChanges(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
IdentitySerializer.deserializeChanges(invocation.arguments.value).asEitherRequest
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityGetMethod.scala
index acebe60236..4a4ef37ee7 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentityGetMethod.scala
@@ -25,7 +25,7 @@ import org.apache.james.jmap.api.model.{Identity, IdentityId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
EMAIL_SUBMISSION, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core._
-import org.apache.james.jmap.json.{IdentitySerializer, ResponseSerializer}
+import org.apache.james.jmap.json.IdentitySerializer
 import org.apache.james.jmap.mail.{IdentityGet, IdentityGetRequest, 
IdentityGetResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
@@ -60,8 +60,7 @@ class IdentityGetMethod @Inject() (identityRepository: 
IdentityRepository,
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, IdentityGetRequest] =
-    IdentitySerializer.deserialize(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    IdentitySerializer.deserialize(invocation.arguments.value).asEitherRequest
 
   private def getIdentities(request: IdentityGetRequest,
                             mailboxSession: MailboxSession): 
SMono[IdentityGetResponse] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentitySetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentitySetMethod.scala
index 30e3ce530b..b885c3ab59 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentitySetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/IdentitySetMethod.scala
@@ -23,12 +23,13 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
EMAIL_SUBMISSION, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, 
SessionTranslator, UuidState}
-import org.apache.james.jmap.json.{IdentitySerializer, ResponseSerializer}
+import org.apache.james.jmap.json.IdentitySerializer
 import org.apache.james.jmap.mail.{IdentitySetRequest, IdentitySetResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 class IdentitySetMethod @Inject()(createPerformer: IdentitySetCreatePerformer,
@@ -41,9 +42,7 @@ class IdentitySetMethod @Inject()(createPerformer: 
IdentitySetCreatePerformer,
   override val requiredCapabilities: Set[CapabilityIdentifier] = 
Set(JMAP_CORE, EMAIL_SUBMISSION)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, IdentitySetRequest] =
-    
IdentitySerializer.deserializeIdentitySetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
-
+    
IdentitySerializer.deserializeIdentitySetRequest(invocation.arguments.value).asEitherRequest
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: 
InvocationWithContext, mailboxSession: MailboxSession, request: 
IdentitySetRequest): SMono[InvocationWithContext] =
     for {
       creationResults <- createPerformer.create(request, mailboxSession)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
index 74f80dd13d..72bf5b5947 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
@@ -21,9 +21,9 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL, JMAP_MDN}
-import org.apache.james.jmap.core.{Invocation, SessionTranslator}
 import org.apache.james.jmap.core.Invocation._
-import org.apache.james.jmap.json.{MDNSerializer, ResponseSerializer}
+import org.apache.james.jmap.core.{Invocation, SessionTranslator}
+import org.apache.james.jmap.json.MDNSerializer
 import org.apache.james.jmap.mail.{BlobId, BlobUnParsableException, 
MDNParseRequest, MDNParseResponse, MDNParseResults, MDNParsed}
 import org.apache.james.jmap.routes.{BlobNotFoundException, BlobResolvers, 
SessionSupplier}
 import org.apache.james.mailbox.model.{MessageId, MultimailboxesSearchQuery, 
SearchQuery}
@@ -35,10 +35,9 @@ import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.message.DefaultMessageBuilder
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
-import java.io.InputStream
 
+import java.io.InputStream
 import javax.inject.Inject
-
 import scala.jdk.OptionConverters._
 import scala.util.{Try, Using}
 
@@ -60,7 +59,7 @@ class MDNParseMethod @Inject()(serializer: MDNSerializer,
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, MDNParseRequest] =
     serializer.deserializeMDNParseRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+      .asEitherRequest
       .flatMap(_.validate)
 
   def computeResponseInvocation(request: MDNParseRequest,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNSendMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNSendMethod.scala
index 8f26bdd35e..143547ca6f 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNSendMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNSendMethod.scala
@@ -22,9 +22,9 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import org.apache.james.jmap.api.model.Identity
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL, JMAP_MDN}
-import org.apache.james.jmap.core.{Invocation, SessionTranslator}
 import org.apache.james.jmap.core.Invocation._
-import org.apache.james.jmap.json.{MDNSerializer, ResponseSerializer}
+import org.apache.james.jmap.core.{Invocation, SessionTranslator}
+import org.apache.james.jmap.json.MDNSerializer
 import org.apache.james.jmap.mail.MDN._
 import org.apache.james.jmap.mail.MDNSend.MDN_ALREADY_SENT_FLAG
 import org.apache.james.jmap.mail._
@@ -47,10 +47,10 @@ import org.apache.james.server.core.MailImpl
 import org.apache.james.util.ReactorUtils
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
+
 import javax.annotation.PreDestroy
 import javax.inject.Inject
 import javax.mail.internet.MimeMessage
-
 import scala.jdk.CollectionConverters._
 import scala.jdk.OptionConverters._
 import scala.util.Try
@@ -106,7 +106,7 @@ class MDNSendMethod @Inject()(serializer: MDNSerializer,
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, MDNSendRequest] =
     serializer.deserializeMDNSendRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+      .asEitherRequest
       .flatMap(_.validate)
 
   private def create(identity: Identity,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxChangesMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxChangesMethod.scala
index 0802cd2b5b..b65b4fc1f3 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxChangesMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxChangesMethod.scala
@@ -25,15 +25,15 @@ import org.apache.james.jmap.api.model.{AccountId => 
JavaAccountId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{CapabilityIdentifier, ErrorCode, 
Invocation, Properties, SessionTranslator, UuidState}
-import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.MailboxSerializer
 import org.apache.james.jmap.mail.{HasMoreChanges, MailboxChangesRequest, 
MailboxChangesResponse}
 import org.apache.james.jmap.method.MailboxChangesMethod.updatedProperties
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
-import javax.inject.Inject
 
+import javax.inject.Inject
 import scala.jdk.CollectionConverters._
 import scala.jdk.OptionConverters._
 
@@ -83,9 +83,7 @@ class MailboxChangesMethod @Inject()(mailboxSerializer: 
MailboxSerializer,
     })
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, MailboxChangesRequest] =
-    
mailboxSerializer.deserializeMailboxChangesRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
-
+    
mailboxSerializer.deserializeMailboxChangesRequest(invocation.arguments.value).asEitherRequest
   private def updateProperties(mailboxChanges: MailboxChanges): 
Option[Properties] =
     if (mailboxChanges.isCountChangesOnly) {
       Some(updatedProperties)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 111fddefc3..bacbf3a1f6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -26,7 +26,7 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JA
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, ErrorCode, 
Invocation, Properties, SessionTranslator, UuidState}
 import org.apache.james.jmap.http.MailboxesProvisioner
-import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.MailboxSerializer
 import org.apache.james.jmap.mail.{Ids, Mailbox, MailboxFactory, MailboxGet, 
MailboxGetRequest, MailboxGetResponse, NotFound, PersonalNamespace, 
Subscriptions, UnparsedMailboxId}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.quotas.{QuotaLoaderWithPreloadedDefault, 
QuotaLoaderWithPreloadedDefaultFactory}
@@ -37,8 +37,8 @@ import org.apache.james.mailbox.{MailboxManager, 
MailboxSession, SubscriptionMan
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
-import javax.inject.Inject
 
+import javax.inject.Inject
 import scala.util.Try
 
 object MailboxGetResults {
@@ -102,9 +102,7 @@ class MailboxGetMethod @Inject() (serializer: 
MailboxSerializer,
     }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, MailboxGetRequest] =
-    serializer.deserializeMailboxGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
-
+    
serializer.deserializeMailboxGetRequest(invocation.arguments.value).asEitherRequest
   private def getMailboxes(capabilities: Set[CapabilityIdentifier],
                            mailboxGetRequest: MailboxGetRequest,
                            mailboxSession: MailboxSession): 
SFlux[MailboxGetResults] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index b4ec36f72d..27a8cdd8ae 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -22,12 +22,13 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{CanCalculateChanges, ErrorCode, Invocation, 
Limit, Position, QueryState, SessionTranslator}
-import org.apache.james.jmap.json.{MailboxQuerySerializer, ResponseSerializer}
+import org.apache.james.jmap.json.MailboxQuerySerializer
 import org.apache.james.jmap.mail.{MailboxQueryRequest, MailboxQueryResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.{MailboxSession, SystemMailboxesProvider}
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.{SFlux, SMono}
+
 import javax.inject.Inject
 
 class MailboxQueryMethod @Inject()(systemMailboxesProvider: 
SystemMailboxesProvider,
@@ -51,8 +52,7 @@ class MailboxQueryMethod @Inject()(systemMailboxesProvider: 
SystemMailboxesProvi
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, MailboxQueryRequest] =
-    MailboxQuerySerializer.deserialize(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
MailboxQuerySerializer.deserialize(invocation.arguments.value).asEitherRequest
 
   private def processRequest(mailboxSession: MailboxSession, invocation: 
Invocation, request: MailboxQueryRequest): SMono[Invocation] =
     
SFlux.fromPublisher(systemMailboxesProvider.getMailboxByRole(request.filter.role,
 mailboxSession.getUser))
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
index 4d8b192005..8e2f9c4b4b 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
@@ -154,13 +154,7 @@ class MailboxSetCreatePerformer @Inject()(serializer: 
MailboxSerializer,
       processingContext.recordCreatedId(ClientId(mailboxCreationId.id), 
ServerId(serverAssignedId))
     }
 
-  private def mailboxSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError =
-    errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox 
object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => 
SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in 
mailbox object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox 
object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
+  private def mailboxSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError = standardError(errors)
 
   private def createMailbox(mailboxSession: MailboxSession,
                             path: MailboxPath,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index 2dce9e51f5..f5d16fcaad 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -25,7 +25,7 @@ import org.apache.james.jmap.api.model.{AccountId => 
JavaAccountId}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_SHARES, JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{Invocation, SessionTranslator, SetError, 
UuidState}
-import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.MailboxSerializer
 import org.apache.james.jmap.mail.{MailboxSetRequest, MailboxSetResponse}
 import 
org.apache.james.jmap.method.MailboxSetCreatePerformer.MailboxCreationResults
 import 
org.apache.james.jmap.method.MailboxSetDeletePerformer.MailboxDeletionResults
@@ -36,6 +36,7 @@ import org.apache.james.mailbox.model.MailboxId
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 case class MailboxHasMailException(mailboxId: MailboxId) extends Exception
@@ -65,8 +66,7 @@ class MailboxSetMethod @Inject()(serializer: 
MailboxSerializer,
   } yield InvocationWithContext(response, creationResults._2)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, MailboxSetRequest] =
-    serializer.deserializeMailboxSetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
serializer.deserializeMailboxSetRequest(invocation.arguments.value).asEitherRequest
 
   private def createResponse(capabilities: Set[CapabilityIdentifier],
                              invocation: Invocation,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala
index 9187531bf3..bdb24de165 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala
@@ -25,15 +25,15 @@ import 
org.apache.james.jmap.api.pushsubscription.PushSubscriptionRepository
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{Ids, Invocation, PushSubscriptionDTO, 
PushSubscriptionGetRequest, PushSubscriptionGetResponse, SessionTranslator, 
UnparsedPushSubscriptionId}
-import org.apache.james.jmap.json.{PushSubscriptionSerializer, 
ResponseSerializer}
+import org.apache.james.jmap.json.PushSubscriptionSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.lifecycle.api.Startable
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
-import javax.inject.Inject
 
+import javax.inject.Inject
 import scala.jdk.CollectionConverters._
 
 case class PushSubscriptionGetResults(results: Seq[PushSubscriptionDTO], 
notFound: Set[UnparsedPushSubscriptionId]) {
@@ -54,8 +54,7 @@ class PushSubscriptionGetMethod 
@Inject()(pushSubscriptionSerializer: PushSubscr
   override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE)
 
   override def getRequest(invocation: Invocation): Either[Exception, 
PushSubscriptionGetRequest] =
-    
pushSubscriptionSerializer.deserializePushSubscriptionGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
pushSubscriptionSerializer.deserializePushSubscriptionGetRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(invocation: InvocationWithContext, session: 
MailboxSession, request: PushSubscriptionGetRequest): 
SMono[InvocationWithContext] =
     request.validateProperties
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetCreatePerformer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetCreatePerformer.scala
index 6a1b460704..4365fc21bc 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetCreatePerformer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetCreatePerformer.scala
@@ -87,13 +87,7 @@ class PushSubscriptionSetCreatePerformer 
@Inject()(pushSubscriptionRepository: P
     case _ => Some(expires)
   }
 
-  private def pushSubscriptionSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError =
-    errors.head match {
-      case (path, Seq()) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in 
PushSubscription object is not valid"))
-      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => 
SetError.invalidArguments(SetErrorDescription(s"Missing '$path' property in 
PushSubscription object"))
-      case (path, Seq(JsonValidationError(Seq(message)))) => 
SetError.invalidArguments(SetErrorDescription(s"'$path' property in 
PushSubscription object is not valid: $message"))
-      case (path, _) => 
SetError.invalidArguments(SetErrorDescription(s"Unknown error on property 
'$path'"))
-    }
+  private def pushSubscriptionSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError = standardError(errors)
 }
 
 class PushSubscriptionSetCreateProcessor @Inject()(webPushClient: 
WebPushClient) {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala
index e3ca62ebe9..ee758cdf6f 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala
@@ -23,12 +23,13 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{Invocation, PushSubscriptionSetRequest, 
PushSubscriptionSetResponse, SessionTranslator}
-import org.apache.james.jmap.json.{PushSubscriptionSerializer, 
ResponseSerializer}
+import org.apache.james.jmap.json.PushSubscriptionSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.lifecycle.api.Startable
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 class PushSubscriptionSetMethod @Inject()(createPerformer: 
PushSubscriptionSetCreatePerformer,
@@ -42,8 +43,7 @@ class PushSubscriptionSetMethod @Inject()(createPerformer: 
PushSubscriptionSetCr
   override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE)
 
   override def getRequest(invocation: Invocation): Either[Exception, 
PushSubscriptionSetRequest] =
-    
pushSubscriptionSerializer.deserializePushSubscriptionSetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
pushSubscriptionSerializer.deserializePushSubscriptionSetRequest(invocation.arguments.value).asEitherRequest
 
   override def doProcess(invocation: InvocationWithContext, mailboxSession: 
MailboxSession, request: PushSubscriptionSetRequest): 
SMono[InvocationWithContext] =
     for {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaChangesMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaChangesMethod.scala
index 87543161eb..8e7fe530c9 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaChangesMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaChangesMethod.scala
@@ -27,7 +27,7 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JM
 import org.apache.james.jmap.core.Id.Id
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, SessionTranslator, 
UuidState}
-import org.apache.james.jmap.json.{QuotaSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.QuotaSerializer
 import org.apache.james.jmap.mail.{JmapQuota, QuotaChangesRequest, 
QuotaChangesResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
@@ -35,6 +35,7 @@ import org.apache.james.mailbox.quota.{QuotaManager, 
UserQuotaRootResolver}
 import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 class QuotaChangesMethod @Inject()(val metricFactory: MetricFactory,
@@ -64,8 +65,7 @@ class QuotaChangesMethod @Inject()(val metricFactory: 
MetricFactory,
       }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, QuotaChangesRequest] =
-    QuotaSerializer.deserializeQuotaChangesRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
QuotaSerializer.deserializeQuotaChangesRequest(invocation.arguments.value).asEitherRequest
 }
 
 case class QuotaChangesResolver(private val quotaManager: QuotaManager,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaGetMethod.scala
index 002418241f..c659a22049 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaGetMethod.scala
@@ -24,7 +24,7 @@ import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JAMES_SHARES, JMAP_CORE, JMAP_QUOTA}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, Properties, 
SessionTranslator}
-import org.apache.james.jmap.json.{QuotaSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.QuotaSerializer
 import org.apache.james.jmap.mail.{CountResourceType, JmapQuota, 
OctetsResourceType, QuotaGetRequest, QuotaIdFactory, QuotaResponseGetResult}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.lifecycle.api.Startable
@@ -68,8 +68,7 @@ class QuotaGetMethod @Inject()(val metricFactory: 
MetricFactory,
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, QuotaGetRequest] =
-    QuotaSerializer.deserializeQuotaGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
QuotaSerializer.deserializeQuotaGetRequest(invocation.arguments.value).asEitherRequest
 
   private def getQuotaGetResponse(request: QuotaGetRequest, username: 
Username, capabilities: Set[CapabilityIdentifier]): 
SMono[QuotaResponseGetResult] =
     jmapQuotaManagerWrapper.list(username, capabilities)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaQueryMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaQueryMethod.scala
index da484f27fe..8d32198587 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaQueryMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/QuotaQueryMethod.scala
@@ -23,15 +23,15 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_QUOTA}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{CanCalculateChanges, ErrorCode, Invocation, 
Limit, Position, QueryState, SessionTranslator}
-import org.apache.james.jmap.json.{QuotaSerializer, ResponseSerializer}
+import org.apache.james.jmap.json.QuotaSerializer
 import org.apache.james.jmap.mail.{JmapQuota, QuotaQueryFilter, 
QuotaQueryRequest, QuotaQueryResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.quota.{QuotaManager, UserQuotaRootResolver}
 import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
-import play.api.libs.json.JsError
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 class QuotaQueryMethod @Inject()(val metricFactory: MetricFactory,
@@ -58,9 +58,7 @@ class QuotaQueryMethod @Inject()(val metricFactory: 
MetricFactory,
       .map(invocationResult => InvocationWithContext(invocationResult, 
invocation.processingContext))
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[Exception, QuotaQueryRequest] =
-    QuotaSerializer.deserializeQuotaQueryRequest(invocation.arguments.value)
-      .asEither
-      .left.map(errors => new 
IllegalArgumentException(ResponseSerializer.serialize(JsError(errors)).toString))
+    
QuotaSerializer.deserializeQuotaQueryRequest(invocation.arguments.value).asEitherRequest
 
   private def processRequest(mailboxSession: MailboxSession, invocation: 
Invocation, request: QuotaQueryRequest, capabilities: 
Set[CapabilityIdentifier]): SMono[Invocation] =
     jmapQuotaManagerWrapper.list(mailboxSession.getUser, capabilities)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadChangesMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadChangesMethod.scala
index 998175f699..0266f05b45 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadChangesMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadChangesMethod.scala
@@ -23,12 +23,13 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{ErrorCode, Invocation, SessionTranslator, 
UuidState}
-import org.apache.james.jmap.json.{ResponseSerializer, ThreadSerializer}
+import org.apache.james.jmap.json.ThreadSerializer
 import org.apache.james.jmap.mail.{HasMoreChanges, ThreadChangesRequest, 
ThreadChangesResponse}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.SMono
+
 import javax.inject.Inject
 
 class ThreadChangesMethod @Inject()(val metricFactory: MetricFactory,
@@ -60,6 +61,5 @@ class ThreadChangesMethod @Inject()(val metricFactory: 
MetricFactory,
     }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, ThreadChangesRequest] =
-    ThreadSerializer.deserializeChanges(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
ThreadSerializer.deserializeChanges(invocation.arguments.value).asEitherRequest
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
index b0069c940c..daee568bda 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/ThreadGetMethod.scala
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, 
JMAP_CORE, JMAP_MAIL}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{AccountId, Invocation, SessionTranslator, 
UuidState}
-import org.apache.james.jmap.json.{ResponseSerializer, ThreadSerializer}
+import org.apache.james.jmap.json.ThreadSerializer
 import org.apache.james.jmap.mail.{Thread, ThreadGetRequest, 
ThreadGetResponse, ThreadNotFound, UnparsedThreadId}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.exception.ThreadNotFoundException
@@ -31,8 +31,8 @@ import org.apache.james.mailbox.model.{ThreadId => 
JavaThreadId}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
 import reactor.core.scala.publisher.{SFlux, SMono}
-import javax.inject.Inject
 
+import javax.inject.Inject
 import scala.util.Try
 
 object ThreadGetResult {
@@ -78,8 +78,7 @@ class ThreadGetMethod @Inject()(val metricFactory: 
MetricFactory,
       .map(InvocationWithContext(_, invocation.processingContext))
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, ThreadGetRequest] =
-    ThreadSerializer.deserialize(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    ThreadSerializer.deserialize(invocation.arguments.value).asEitherRequest
 
   private def getThreadResponse(threadGetRequest: ThreadGetRequest,
                                 mailboxSession: MailboxSession): 
SFlux[ThreadGetResult] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 246cb19ce2..a07c9384c8 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -24,7 +24,7 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JM
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.UuidState.INSTANCE
 import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, 
Properties, SessionTranslator}
-import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
+import org.apache.james.jmap.json.VacationSerializer
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.vacation.VacationResponse.UNPARSED_SINGLETON
 import org.apache.james.jmap.vacation.{UnparsedVacationResponseId, 
VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, 
VacationResponseNotFound}
@@ -83,9 +83,7 @@ class VacationResponseGetMethod @Inject()(vacationService: 
VacationService,
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, VacationResponseGetRequest] =
-    
VacationSerializer.deserializeVacationResponseGetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
-
+    
VacationSerializer.deserializeVacationResponseGetRequest(invocation.arguments.value).asEitherRequest
   private def getVacationResponse(vacationResponseGetRequest: 
VacationResponseGetRequest,
                                   mailboxSession: MailboxSession): 
SFlux[VacationResponseGetResult] =
     vacationResponseGetRequest.ids match {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index 0acfe169df..813938beaa 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -29,7 +29,7 @@ import 
org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JM
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{Invocation, SessionTranslator, UuidState}
-import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
+import org.apache.james.jmap.json.VacationSerializer
 import 
org.apache.james.jmap.method.VacationResponseSetMethod.VACATION_RESPONSE_PATCH_OBJECT_KEY
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.vacation.{VacationResponseSetError, 
VacationResponseSetRequest, VacationResponseSetResponse, 
VacationResponseUpdateResponse}
@@ -38,6 +38,7 @@ import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.vacation.api.{VacationPatch, VacationService, 
AccountId => VacationAccountId}
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
+
 import javax.inject.{Inject, Named}
 
 object VacationResponseUpdateResults {
@@ -97,8 +98,7 @@ class VacationResponseSetMethod 
@Inject()(@Named(InjectionKeys.JMAP) eventBus: E
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: 
Invocation): Either[IllegalArgumentException, VacationResponseSetRequest] =
-    
VacationSerializer.deserializeVacationResponseSetRequest(invocation.arguments.value)
-      .asEither.left.map(ResponseSerializer.asException)
+    
VacationSerializer.deserializeVacationResponseSetRequest(invocation.arguments.value).asEitherRequest
 
   private def update(mailboxSession: MailboxSession, 
vacationResponseSetRequest: VacationResponseSetRequest): 
SMono[VacationResponseUpdateResults] = {
     SFlux.fromIterable(vacationResponseSetRequest.parsePatch()
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/package.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/package.scala
new file mode 100644
index 0000000000..1a11ad529f
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/package.scala
@@ -0,0 +1,48 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ * http://www.apache.org/licenses/LICENSE-2.0                   *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap
+
+import org.apache.james.jmap.core.SetError
+import org.apache.james.jmap.core.SetError.SetErrorDescription
+import org.apache.james.jmap.json.ResponseSerializer
+import play.api.libs.json.{JsError, JsPath, JsResult, JsonValidationError}
+
+import scala.language.implicitConversions
+
+package object method {
+
+  def standardErrorMessage(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): String =
+    errors.head match {
+      case (path, Seq()) => s"'$path' property is not valid"
+      case (path, Seq(JsonValidationError(Seq("error.path.missing")))) => 
s"Missing '$path' property"
+      case (path, Seq(JsonValidationError(Seq("error.expected.jsarray")))) => 
s"'$path' property need to be an array"
+      case (path, Seq(JsonValidationError(Seq(message)))) => s"'$path' 
property is not valid: $message"
+      case (path, _) => s"Unknown error on property '$path'"
+      case _ => ResponseSerializer.serialize(JsError(errors)).toString()
+    }
+
+  def standardError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): SetError =
+    
SetError.invalidArguments(SetErrorDescription(standardErrorMessage(errors)))
+
+  implicit class AsEitherRequest[T](val jsResult: JsResult[T]) {
+    def asEitherRequest: Either[IllegalArgumentException, T] =
+      jsResult.asEither.left.map(errors => new 
IllegalArgumentException(standardErrorMessage(errors)))
+  }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to