Repository navigation
Storage retries don't work as expected #2098
Description
Activity
- addedapi: storageIssues related to the Cloud Storage API.Issues related to the Cloud Storage API.priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.Important issue which blocks shipping the next release. Will be fixed prior to next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
on Jun 1, 2017 Update: the reason the details field in the GoogleJsonResponseException isn't set is that content type of the error response from the GCS service is
text/html; charset=UTF-8, butpublic static GoogleJsonResponseException from(JsonFactory jsonFactory, HttpResponse response)is looking for a content type ofapplication/json; charset=UTF-8in order to create the details object.From
com.google.api.client.googleapis.json.GoogleJsonResponseException:public static GoogleJsonResponseException from(JsonFactory jsonFactory, HttpResponse response) { HttpResponseException.Builder builder = new HttpResponseException.Builder( response.getStatusCode(), response.getStatusMessage(), response.getHeaders()); // details Preconditions.checkNotNull(jsonFactory); GoogleJsonError details = null; String detailString = null; try { if (!response.isSuccessStatusCode() && HttpMediaType.equalsIgnoreParameters(Json.MEDIA_TYPE, response.getContentType()) && response.getContent() != null) { JsonParser parser = null; try { parser = jsonFactory.createJsonParser(response.getContent()); JsonToken currentToken = parser.getCurrentToken(); // token is null at start, so get next token if (currentToken == null) { currentToken = parser.nextToken(); } // check for empty content if (currentToken != null) { // make sure there is an "error" key parser.skipToKey("error"); if (parser.getCurrentToken() != JsonToken.END_OBJECT) { details = parser.parseAndClose(GoogleJsonError.class); detailString = details.toPrettyString(); } } } catch (IOException exception) { // it would be bad to throw an exception while throwing an exception exception.printStackTrace(); } finally { if (parser == null) { response.ignore(); } else if (details == null) { parser.close(); } } } else { detailString = response.parseAsString(); } } catch (IOException exception) { // it would be bad to throw an exception while throwing an exception exception.printStackTrace(); } // message StringBuilder message = HttpResponseException.computeMessageBuffer(response); if (!com.google.api.client.util.Strings.isNullOrEmpty(detailString)) { message.append(StringUtils.LINE_SEPARATOR).append(detailString); builder.setContent(detailString); } builder.setMessage(message.toString()); // result return new GoogleJsonResponseException(builder, details); }
The second condition in that if statement evaluates to false so the details object remains null.
Reacted by Josip Rakić@Dima1224, thanks for your investigation! If you still have it, can you please post the actual response you got from the server (that one with "test/html" content-type), and, ideally, the corresponsing request.
@vam-google - sorry but I don't have those details anymore, nor do I have the setup I used to reproduce this issue. At a high level, I simply made requests to GCS in an infinite loop, set a few breakpoints, and waited for an error. Hope this helps!
Dima1224 Your response is a gem!
I've had stumbled upon issue when upgrading google storage library from 2.6 to 2.37 and I had my hoverfly tests setup to return 3 503s with content type "text/plain". Storage service wouldn't retry. Until I changed the hoverfly to return content type of "application/json"Thank you so much!
Happy to see this helped you! And a little disappointed this still isn't fixed 😅
- added a commit that references this issue
on Feb 20, 2026 - added a commit that references this issue
on Mar 23, 2026 - added 2 commits that reference this issue
on Apr 29, 2026 - added a commit that references this issue
on Jul 13, 2026
I have been getting 503 errors from GCS while downloading content and would expect the storage library to retry, but it does not. After a bit of debugging, it turns out that the issue is that GoogleJsonResponseException doesn't have its details field set and when that is the case the StorageException it is converted to is marked as not retriable.
Here's the code snippet which is the problem from BaseHttpServiceException:
In this snippet, the list of retriableErrors is only referenced if getDetails() returns a non-null result or if we're dealing with an HttpResponseException which isn't a GoogleJsonResponseException.
In my case, the error is a GoogleJsonResponseException with a 503 status code but no details field (not sure if this is a bug upstream somewhere). As a result,
BaseServiceException.isRetryable(idempotent, exception)is called to determine if the exception is retriable, which returns false.I'm using v1.0.2 of the library.
Here's the stack trace: