square/okhttp · error · IOException

Unexpected code

Error message

Unexpected code 

What it means

Same idiom, this time in the UploadProgress recipe after uploading a PNG to Imgur: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);`. The `response` object's toString carries the Imgur error code/message. The wrapping `ProgressRequestBody` only instruments bytes written; it does not change success semantics.

Solutions

  1. Register at https://api.imgur.com/oauth2/addclient and replace `IMGUR_CLIENT_ID` with your own; for user-scoped uploads use OAuth2 `Bearer <access_token>` instead of `Client-ID`.
  2. Verify the upload file exists and is a valid PNG before building the request (guard against empty/malformed body that yields 400).
  3. Before throwing, read and log `response.body().string()` and `response.code()` so you can tell 401 (auth) from 429 (rate) from 400 (payload).
  4. Respect `X-RateLimit-ClientRemaining`/`X-RateLimit-UserRemaining` headers and back off on 429.

Example fix

// before
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());

// after
try (Response response = client.newCall(request).execute()) {
  String body = response.body() != null ? response.body().string() : "";
  if (!response.isSuccessful()) {
    throw new IOException("Imgur upload failed: HTTP " + response.code()
        + " (rateLimit-client=" + response.header("X-RateLimit-ClientRemaining") + "): " + body);
  }
  System.out.println(body);
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: validate credentials and file before the upload.
String clientId = System.getenv("IMGUR_CLIENT_ID");
if (clientId == null || clientId.isBlank()) {
  throw new IllegalStateException("IMGUR_CLIENT_ID env var not set.");
}
File file = new File("docs/images/logo-square.png");
if (!file.isFile() || file.length() == 0) {
  throw new IllegalArgumentException("Missing or empty upload file: " + file);
}
RequestBody requestBody = RequestBody.create(file, MEDIA_TYPE_PNG);
Request request = new Request.Builder()
    .header("Authorization", "Client-ID " + clientId)
    .url("https://api.imgur.com/3/image")
    .post(new ProgressRequestBody(requestBody, progressListener))
    .build();

// Post-call: classify Imgur-specific status.
try (Response response = client.newCall(request).execute()) {
  String body = response.body() != null ? response.body().string() : "";
  if (response.code() == 401 || response.code() == 403) {
    throw new AuthException("Imgur rejected Client-ID (" + response.code() + "): " + body);
  }
  if (response.code() == 429) {
    throw new RateLimitedException(response.header("X-RateLimit-UserReset"));
  }
  if (!response.isSuccessful()) throw new IOException("Imgur " + response.code() + ": " + body);
}

Type guard

private static boolean imgurAuthFailed(Response r) {
  return r.code() == 401 || r.code() == 403;
}
private static boolean imgurRateLimited(Response r) {
  return r.code() == 429
      || "0".equals(r.header("X-RateLimit-UserRemaining"));
}

Try / catch

try (Response response = client.newCall(request).execute()) {
  String body = response.body() != null ? response.body().string() : "";
  if (imgurAuthFailed(response)) {
    throw new AuthException("Imgur auth failed (" + response.code() + "): " + body);
  }
  if (imgurRateLimited(response)) {
    throw new RateLimitedException(response.header("X-RateLimit-UserReset"));
  }
  if (!response.isSuccessful()) {
    throw new IOException("Imgur upload failed: HTTP " + response.code() + ": " + body);
  }
  System.out.println(body);
} catch (RateLimitedException e) {
  sleepUntil(e.resetEpoch()); // then optionally retry
} catch (AuthException e) {
  // surface to operator — credentials must be rotated manually
  throw e;
}

Prevention

When it happens

Trigger: POST to `https://api.imgur.com/3/image` with `Authorization: Client-ID 9199fdef135c122` (a sample/revoked credential) returns 401/403 because Imgur now requires per-application registration and has retired widely-shared demo client IDs. Also fires on 429 (Imgur rate limits, ~12,500/day per app, 50/min anonymous), 400 (missing/malformed multipart body or unsupported media), or 404 if the endpoint path changes.

Common situations: Running the recipe as-is with the leaked `IMGUR_CLIENT_ID`; registering an Imgur app but forgetting to pass the new client ID; file `docs/images/logo-square.png` missing or unreadable so the body is empty (server returns 400); Imgur migrating endpoint versions; Imgur blocking uploads from cloud/datacenter IPs.

Related errors


AI-assisted analysis of square/okhttp@91a8b34c6f (2026-08-10). Data as JSON: /api/errors/4368d499451ca672. Report an issue: GitHub.

Appendix: source

Thrown at samples/guide/src/main/java/okhttp3/recipes/UploadProgress.java:75

          if (contentLength != -1) {
            System.out.format("%d%% done\n", (100 * bytesWritten) / contentLength);
          }
        }
      }
    };

    RequestBody requestBody = RequestBody.create(
      new File("docs/images/logo-square.png"),
      MEDIA_TYPE_PNG);

    Request request = new Request.Builder()
      .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
      .url("https://api.imgur.com/3/image")
      .post(new ProgressRequestBody(requestBody, progressListener))
      .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

  public static void main(String... args) throws Exception {
    new UploadProgress().run();
  }

  private static class ProgressRequestBody extends RequestBody {

    private final ProgressListener progressListener;
    private final RequestBody delegate;

    public ProgressRequestBody(RequestBody delegate, ProgressListener progressListener) {
      this.delegate = delegate;
      this.progressListener = progressListener;
    }

View on GitHub (pinned to 91a8b34c6f)