square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
OkHttp's Response.isSuccessful() returns true only when the HTTP status code is in [200, 300). The recipe at RequestBodyCompression.java:59 wraps any non-2xx response in an IOException with the literal message 'Unexpected code ' concatenated with the Response's toString(). This is a sample-recipe convention, not a dedicated OkHttp exception type, so the stack trace is the only signal of which upstream API actually rejected the call.
Source
Thrown at samples/guide/src/main/java/okhttp3/recipes/RequestBodyCompression.java:59
private final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new GzipRequestInterceptor())
.build();
private final Moshi moshi = new Moshi.Builder().build();
private final JsonAdapter<Map<String, String>> mapJsonAdapter = moshi.adapter(
Types.newParameterizedType(Map.class, String.class, String.class));
public void run() throws Exception {
Map<String, String> requestBody = new LinkedHashMap<>();
requestBody.put("longUrl", "https://publicobject.com/2014/12/04/html-formatting-javadocs/");
RequestBody jsonRequestBody = RequestBody.create(
mapJsonAdapter.toJson(requestBody), MEDIA_TYPE_JSON);
Request request = new Request.Builder()
.url("https://www.googleapis.com/urlshortener/v1/url?key=" + GOOGLE_API_KEY)
.post(jsonRequestBody)
.build();
try (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 RequestBodyCompression().run();
}
/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */
static class GzipRequestInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()View on GitHub (pinned to 4fc0831380)
Solutions
- Replace the Google URL Shortener URL with a live endpoint (e.g. Firebase Dynamic Links) or a local mock server that returns 200, since the URL Shortener API is fully shut down.
- Inspect the failing Response before throwing — log response.code(), response.headers(), and response.body().string() — to identify 401/403/404/415 precisely.
- If keeping GzipRequestInterceptor, confirm the upstream server supports Content-Encoding: gzip on requests, otherwise drop the interceptor or gate it on a per-host condition.
- Obtain a fresh Google API key from https://console.developers.google.com/project and enable the relevant API for it; never ship the sample key 'AIzaSyAx2WZYe0My0My0i-uGurpvraYJxO7XNbwiGs'.
Example fix
// before
try (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()) {
if (!response.isSuccessful()) {
String body = response.peekBody(2048).string();
throw new IOException("Unexpected code " + response.code()
+ " for " + request.url() + ": " + body);
}
System.out.println(response.body().string());
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the endpoint is alive and the key is set before the call
String apiKey = System.getenv("GOOGLE_API_KEY");
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalStateException("GOOGLE_API_KEY env var not set");
}
// Prefer a live endpoint; the URL Shortener API is shut down.
if (request.url().toString().contains("urlshortener/v1/url")) {
throw new IllegalStateException("URL Shortener API is deprecated; switch endpoint");
} Try / catch
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("HTTP " + response.code() + " "
+ response.peekBody(2048).string());
}
handle(response.body().string());
} catch (IOException e) {
// distinguish network failure (no response) from non-2xx (has code)
throw new RuntimeException("URL shortener call failed: " + e.getMessage(), e);
} Prevention
- Never copy sample API keys into production; read them from env/secrets.
- If using GzipRequestInterceptor, confirm the server accepts gzip request bodies (very few do).
- Pin to a live endpoint — deprecated Google APIs will always return non-2xx.
- Always include response.code() and a body snippet in thrown IOExceptions for diagnosis.
When it happens
Trigger: Posting JSON to https://www.googleapis.com/urlshortener/v1/url?key=<key> after Google retired the URL Shortener API (March 2018) — server returns 404/410/403. Also triggered when the GzipRequestInterceptor (RequestBodyCompression.java:70-82) sets Content-Encoding: gzip and Google's front end refuses the gzipped request body with a 400/415. A revoked/invalid GOOGLE_API_KEY (hardcoded at line 38) yields 403 Forbidden.
Common situations: Running the bundled OkHttp samples verbatim against a now-deprecated Google endpoint; copying the recipe's hardcoded API key into production; enabling client-side request-body gzip against a server that does not advertise gzip request-encoding support; switching from http to a vhost that returns a non-2xx error page.
Related errors
AI-assisted analysis of square/okhttp@4fc0831380 (2026-08-04).
Data as JSON: /data/errors/ebc3523e61c72ff2.json.
Report an issue: GitHub.