We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
errors.py: Add support for JSON content that has `errors` instead of `detail` or `details`
Thanks for stopping by to let us know something could be better!

**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response.

 **Is your feature request related to a problem? Please describe.**

We have been using [errors.py](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py) to raise HttpError errors. Unfortunately, many of our instances have content that matches the following format:
```
{
  error: {
    errors: {
    }
  }
}
```
versus what [_get_reason](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py#L64) is looking for i.e. only one of `detail`, `details`, or `message`
This results us seeing the errors being logged but not the details:

```
<HttpError 503 when requesting url returned "Internal error. Please try again.">
```

 **Describe the solution you'd like**

The solution involves modifying [_get_reason](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py#L64) to also support `error_detail_keyword` with an additional value of `errors`. This modifies the order to: `detail`, `details`, `errors`, `message`

 **Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
 **Additional context**
Add any other context or screenshots about the feature request here.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `googleapiclient/errors.py`
Content:
```
1 # Copyright 2014 Google Inc. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """Errors for the library.
16 
17 All exceptions defined by the library
18 should be defined in this file.
19 """
20 from __future__ import absolute_import
21 
22 __author__ = "jcgregorio@google.com (Joe Gregorio)"
23 
24 import json
25 
26 from googleapiclient import _helpers as util
27 
28 
29 class Error(Exception):
30     """Base error for this module."""
31 
32     pass
33 
34 
35 class HttpError(Error):
36     """HTTP data was invalid or unexpected."""
37 
38     @util.positional(3)
39     def __init__(self, resp, content, uri=None):
40         self.resp = resp
41         if not isinstance(content, bytes):
42             raise TypeError("HTTP content should be bytes")
43         self.content = content
44         self.uri = uri
45         self.error_details = ""
46         self._get_reason()
47 
48     @property
49     def status_code(self):
50         """Return the HTTP status code from the response content."""
51         return self.resp.status
52 
53     def _get_reason(self):
54         """Calculate the reason for the error from the response content."""
55         reason = self.resp.reason
56         try:
57             try:
58                 data = json.loads(self.content.decode("utf-8"))
59             except json.JSONDecodeError:
60                 # In case it is not json
61                 data = self.content.decode("utf-8")
62             if isinstance(data, dict):
63                 reason = data["error"]["message"]
64                 error_detail_keyword = next((kw for kw in ["detail", "details", "message"] if kw in data["error"]), "")
65                 if error_detail_keyword:
66                     self.error_details = data["error"][error_detail_keyword]
67             elif isinstance(data, list) and len(data) > 0:
68                 first_error = data[0]
69                 reason = first_error["error"]["message"]
70                 if "details" in first_error["error"]:
71                     self.error_details = first_error["error"]["details"]
72             else:
73                 self.error_details = data
74         except (ValueError, KeyError, TypeError):
75             pass
76         if reason is None:
77             reason = ""
78         return reason
79 
80     def __repr__(self):
81         reason = self._get_reason()
82         if self.error_details:
83             return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % (
84                 self.resp.status,
85                 self.uri,
86                 reason.strip(),
87                 self.error_details,
88             )
89         elif self.uri:
90             return '<HttpError %s when requesting %s returned "%s">' % (
91                 self.resp.status,
92                 self.uri,
93                 self._get_reason().strip(),
94             )
95         else:
96             return '<HttpError %s "%s">' % (self.resp.status, self._get_reason())
97 
98     __str__ = __repr__
99 
100 
101 class InvalidJsonError(Error):
102     """The JSON returned could not be parsed."""
103 
104     pass
105 
106 
107 class UnknownFileType(Error):
108     """File type unknown or unexpected."""
109 
110     pass
111 
112 
113 class UnknownLinkType(Error):
114     """Link type unknown or unexpected."""
115 
116     pass
117 
118 
119 class UnknownApiNameOrVersion(Error):
120     """No API with that name and version exists."""
121 
122     pass
123 
124 
125 class UnacceptableMimeTypeError(Error):
126     """That is an unacceptable mimetype for this operation."""
127 
128     pass
129 
130 
131 class MediaUploadSizeError(Error):
132     """Media is larger than the method can accept."""
133 
134     pass
135 
136 
137 class ResumableUploadError(HttpError):
138     """Error occurred during resumable upload."""
139 
140     pass
141 
142 
143 class InvalidChunkSizeError(Error):
144     """The given chunksize is not valid."""
145 
146     pass
147 
148 
149 class InvalidNotificationError(Error):
150     """The channel Notification is invalid."""
151 
152     pass
153 
154 
155 class BatchError(HttpError):
156     """Error occurred during batch operations."""
157 
158     @util.positional(2)
159     def __init__(self, reason, resp=None, content=None):
160         self.resp = resp
161         self.content = content
162         self.reason = reason
163 
164     def __repr__(self):
165         if getattr(self.resp, "status", None) is None:
166             return '<BatchError "%s">' % (self.reason)
167         else:
168             return '<BatchError %s "%s">' % (self.resp.status, self.reason)
169 
170     __str__ = __repr__
171 
172 
173 class UnexpectedMethodError(Error):
174     """Exception raised by RequestMockBuilder on unexpected calls."""
175 
176     @util.positional(1)
177     def __init__(self, methodId=None):
178         """Constructor for an UnexpectedMethodError."""
179         super(UnexpectedMethodError, self).__init__(
180             "Received unexpected call %s" % methodId
181         )
182 
183 
184 class UnexpectedBodyError(Error):
185     """Exception raised by RequestMockBuilder on unexpected bodies."""
186 
187     def __init__(self, expected, provided):
188         """Constructor for an UnexpectedMethodError."""
189         super(UnexpectedBodyError, self).__init__(
190             "Expected: [%s] - Provided: [%s]" % (expected, provided)
191         )
192
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py
--- a/googleapiclient/errors.py
+++ b/googleapiclient/errors.py
@@ -61,7 +61,7 @@
                 data = self.content.decode("utf-8")
             if isinstance(data, dict):
                 reason = data["error"]["message"]
-                error_detail_keyword = next((kw for kw in ["detail", "details", "message"] if kw in data["error"]), "")
+                error_detail_keyword = next((kw for kw in ["detail", "details", "errors", "message"] if kw in data["error"]), "")
                 if error_detail_keyword:
                     self.error_details = data["error"][error_detail_keyword]
             elif isinstance(data, list) and len(data) > 0:

{"golden_diff": "diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py\n--- a/googleapiclient/errors.py\n+++ b/googleapiclient/errors.py\n@@ -61,7 +61,7 @@\n                 data = self.content.decode(\"utf-8\")\n             if isinstance(data, dict):\n                 reason = data[\"error\"][\"message\"]\n-                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"message\"] if kw in data[\"error\"]), \"\")\n+                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"errors\", \"message\"] if kw in data[\"error\"]), \"\")\n                 if error_detail_keyword:\n                     self.error_details = data[\"error\"][error_detail_keyword]\n             elif isinstance(data, list) and len(data) > 0:\n", "issue": "errors.py: Add support for JSON content that has `errors` instead of `detail` or `details`\nThanks for stopping by to let us know something could be better!\r\n\r\n**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response.\r\n\r\n **Is your feature request related to a problem? Please describe.**\r\n\r\nWe have been using [errors.py](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py) to raise HttpError errors. Unfortunately, many of our instances have content that matches the following format:\r\n```\r\n{\r\n  error: {\r\n    errors: {\r\n    }\r\n  }\r\n}\r\n```\r\nversus what [_get_reason](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py#L64) is looking for i.e. only one of `detail`, `details`, or `message`\r\nThis results us seeing the errors being logged but not the details:\r\n\r\n```\r\n<HttpError 503 when requesting url returned \"Internal error. Please try again.\">\r\n```\r\n\r\n **Describe the solution you'd like**\r\n\r\nThe solution involves modifying [_get_reason](https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/errors.py#L64) to also support `error_detail_keyword` with an additional value of `errors`. This modifies the order to: `detail`, `details`, `errors`, `message`\r\n\r\n **Describe alternatives you've considered**\r\nA clear and concise description of any alternative solutions or features you've considered.\r\n **Additional context**\r\nAdd any other context or screenshots about the feature request here.\r\n\n", "before_files": [{"content": "# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Errors for the library.\n\nAll exceptions defined by the library\nshould be defined in this file.\n\"\"\"\nfrom __future__ import absolute_import\n\n__author__ = \"jcgregorio@google.com (Joe Gregorio)\"\n\nimport json\n\nfrom googleapiclient import _helpers as util\n\n\nclass Error(Exception):\n    \"\"\"Base error for this module.\"\"\"\n\n    pass\n\n\nclass HttpError(Error):\n    \"\"\"HTTP data was invalid or unexpected.\"\"\"\n\n    @util.positional(3)\n    def __init__(self, resp, content, uri=None):\n        self.resp = resp\n        if not isinstance(content, bytes):\n            raise TypeError(\"HTTP content should be bytes\")\n        self.content = content\n        self.uri = uri\n        self.error_details = \"\"\n        self._get_reason()\n\n    @property\n    def status_code(self):\n        \"\"\"Return the HTTP status code from the response content.\"\"\"\n        return self.resp.status\n\n    def _get_reason(self):\n        \"\"\"Calculate the reason for the error from the response content.\"\"\"\n        reason = self.resp.reason\n        try:\n            try:\n                data = json.loads(self.content.decode(\"utf-8\"))\n            except json.JSONDecodeError:\n                # In case it is not json\n                data = self.content.decode(\"utf-8\")\n            if isinstance(data, dict):\n                reason = data[\"error\"][\"message\"]\n                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"message\"] if kw in data[\"error\"]), \"\")\n                if error_detail_keyword:\n                    self.error_details = data[\"error\"][error_detail_keyword]\n            elif isinstance(data, list) and len(data) > 0:\n                first_error = data[0]\n                reason = first_error[\"error\"][\"message\"]\n                if \"details\" in first_error[\"error\"]:\n                    self.error_details = first_error[\"error\"][\"details\"]\n            else:\n                self.error_details = data\n        except (ValueError, KeyError, TypeError):\n            pass\n        if reason is None:\n            reason = \"\"\n        return reason\n\n    def __repr__(self):\n        reason = self._get_reason()\n        if self.error_details:\n            return '<HttpError %s when requesting %s returned \"%s\". Details: \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                reason.strip(),\n                self.error_details,\n            )\n        elif self.uri:\n            return '<HttpError %s when requesting %s returned \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                self._get_reason().strip(),\n            )\n        else:\n            return '<HttpError %s \"%s\">' % (self.resp.status, self._get_reason())\n\n    __str__ = __repr__\n\n\nclass InvalidJsonError(Error):\n    \"\"\"The JSON returned could not be parsed.\"\"\"\n\n    pass\n\n\nclass UnknownFileType(Error):\n    \"\"\"File type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownLinkType(Error):\n    \"\"\"Link type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownApiNameOrVersion(Error):\n    \"\"\"No API with that name and version exists.\"\"\"\n\n    pass\n\n\nclass UnacceptableMimeTypeError(Error):\n    \"\"\"That is an unacceptable mimetype for this operation.\"\"\"\n\n    pass\n\n\nclass MediaUploadSizeError(Error):\n    \"\"\"Media is larger than the method can accept.\"\"\"\n\n    pass\n\n\nclass ResumableUploadError(HttpError):\n    \"\"\"Error occurred during resumable upload.\"\"\"\n\n    pass\n\n\nclass InvalidChunkSizeError(Error):\n    \"\"\"The given chunksize is not valid.\"\"\"\n\n    pass\n\n\nclass InvalidNotificationError(Error):\n    \"\"\"The channel Notification is invalid.\"\"\"\n\n    pass\n\n\nclass BatchError(HttpError):\n    \"\"\"Error occurred during batch operations.\"\"\"\n\n    @util.positional(2)\n    def __init__(self, reason, resp=None, content=None):\n        self.resp = resp\n        self.content = content\n        self.reason = reason\n\n    def __repr__(self):\n        if getattr(self.resp, \"status\", None) is None:\n            return '<BatchError \"%s\">' % (self.reason)\n        else:\n            return '<BatchError %s \"%s\">' % (self.resp.status, self.reason)\n\n    __str__ = __repr__\n\n\nclass UnexpectedMethodError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected calls.\"\"\"\n\n    @util.positional(1)\n    def __init__(self, methodId=None):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedMethodError, self).__init__(\n            \"Received unexpected call %s\" % methodId\n        )\n\n\nclass UnexpectedBodyError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected bodies.\"\"\"\n\n    def __init__(self, expected, provided):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedBodyError, self).__init__(\n            \"Expected: [%s] - Provided: [%s]\" % (expected, provided)\n        )\n", "path": "googleapiclient/errors.py"}], "after_files": [{"content": "# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Errors for the library.\n\nAll exceptions defined by the library\nshould be defined in this file.\n\"\"\"\nfrom __future__ import absolute_import\n\n__author__ = \"jcgregorio@google.com (Joe Gregorio)\"\n\nimport json\n\nfrom googleapiclient import _helpers as util\n\n\nclass Error(Exception):\n    \"\"\"Base error for this module.\"\"\"\n\n    pass\n\n\nclass HttpError(Error):\n    \"\"\"HTTP data was invalid or unexpected.\"\"\"\n\n    @util.positional(3)\n    def __init__(self, resp, content, uri=None):\n        self.resp = resp\n        if not isinstance(content, bytes):\n            raise TypeError(\"HTTP content should be bytes\")\n        self.content = content\n        self.uri = uri\n        self.error_details = \"\"\n        self._get_reason()\n\n    @property\n    def status_code(self):\n        \"\"\"Return the HTTP status code from the response content.\"\"\"\n        return self.resp.status\n\n    def _get_reason(self):\n        \"\"\"Calculate the reason for the error from the response content.\"\"\"\n        reason = self.resp.reason\n        try:\n            try:\n                data = json.loads(self.content.decode(\"utf-8\"))\n            except json.JSONDecodeError:\n                # In case it is not json\n                data = self.content.decode(\"utf-8\")\n            if isinstance(data, dict):\n                reason = data[\"error\"][\"message\"]\n                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"errors\", \"message\"] if kw in data[\"error\"]), \"\")\n                if error_detail_keyword:\n                    self.error_details = data[\"error\"][error_detail_keyword]\n            elif isinstance(data, list) and len(data) > 0:\n                first_error = data[0]\n                reason = first_error[\"error\"][\"message\"]\n                if \"details\" in first_error[\"error\"]:\n                    self.error_details = first_error[\"error\"][\"details\"]\n            else:\n                self.error_details = data\n        except (ValueError, KeyError, TypeError):\n            pass\n        if reason is None:\n            reason = \"\"\n        return reason\n\n    def __repr__(self):\n        reason = self._get_reason()\n        if self.error_details:\n            return '<HttpError %s when requesting %s returned \"%s\". Details: \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                reason.strip(),\n                self.error_details,\n            )\n        elif self.uri:\n            return '<HttpError %s when requesting %s returned \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                self._get_reason().strip(),\n            )\n        else:\n            return '<HttpError %s \"%s\">' % (self.resp.status, self._get_reason())\n\n    __str__ = __repr__\n\n\nclass InvalidJsonError(Error):\n    \"\"\"The JSON returned could not be parsed.\"\"\"\n\n    pass\n\n\nclass UnknownFileType(Error):\n    \"\"\"File type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownLinkType(Error):\n    \"\"\"Link type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownApiNameOrVersion(Error):\n    \"\"\"No API with that name and version exists.\"\"\"\n\n    pass\n\n\nclass UnacceptableMimeTypeError(Error):\n    \"\"\"That is an unacceptable mimetype for this operation.\"\"\"\n\n    pass\n\n\nclass MediaUploadSizeError(Error):\n    \"\"\"Media is larger than the method can accept.\"\"\"\n\n    pass\n\n\nclass ResumableUploadError(HttpError):\n    \"\"\"Error occurred during resumable upload.\"\"\"\n\n    pass\n\n\nclass InvalidChunkSizeError(Error):\n    \"\"\"The given chunksize is not valid.\"\"\"\n\n    pass\n\n\nclass InvalidNotificationError(Error):\n    \"\"\"The channel Notification is invalid.\"\"\"\n\n    pass\n\n\nclass BatchError(HttpError):\n    \"\"\"Error occurred during batch operations.\"\"\"\n\n    @util.positional(2)\n    def __init__(self, reason, resp=None, content=None):\n        self.resp = resp\n        self.content = content\n        self.reason = reason\n\n    def __repr__(self):\n        if getattr(self.resp, \"status\", None) is None:\n            return '<BatchError \"%s\">' % (self.reason)\n        else:\n            return '<BatchError %s \"%s\">' % (self.resp.status, self.reason)\n\n    __str__ = __repr__\n\n\nclass UnexpectedMethodError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected calls.\"\"\"\n\n    @util.positional(1)\n    def __init__(self, methodId=None):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedMethodError, self).__init__(\n            \"Received unexpected call %s\" % methodId\n        )\n\n\nclass UnexpectedBodyError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected bodies.\"\"\"\n\n    def __init__(self, expected, provided):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedBodyError, self).__init__(\n            \"Expected: [%s] - Provided: [%s]\" % (expected, provided)\n        )\n", "path": "googleapiclient/errors.py"}]}