square/okhttp · error · IOException

Unexpected code

Error message

Unexpected code 

What it means

Thrown by the recipe's own guard: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response)`. OkHttp deliberately does NOT throw for HTTP error status codes (4xx/5xx); it returns them as Response objects so the application can decide. `response.isSuccessful()` is true only when `code` is in 200..299. This IOException therefore fires whenever the GitHub API answers with anything outside the 2xx range.

Solutions

  1. Read response.code() and response.body() before throwing to see the exact status and GitHub error message.
  2. Point the URL at a repo that actually exists, or use the generic 'repos/{owner}/{name}' path for your own repo.
  3. Add an OAuth/token Authorization header (or authenticator) to raise the GitHub rate limit if you are getting 403.
  4. Keep the custom User-Agent header shown in the sample; GitHub rejects requests without one.
  5. Switch from throwing to branching on the code so 3xx/4xx can be handled explicitly.

Example fix

// before
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

// after
if (response.code() == 404) {
  System.err.println("Repo not found: " + response.request().url());
  return;
}
if (!response.isSuccessful()) {
  throw new IOException("HTTP " + response.code() + ": " + response.body().string());
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate BEFORE consuming the body: branch on the code rather than throwing blindly.
try (Response response = client.newCall(request).execute()) {
  int code = response.code();
  if (code == 404) { /* missing repo */ return; }
  if (code == 403 || code == 429) { /* rate limited; back off / add token */ return; }
  if (!response.isSuccessful()) {
    throw new IOException("GitHub HTTP " + code + ": " + response.body().string());
  }
  // safe to use body
}

Type guard

// Java has no runtime type guards; narrow on status instead.
static boolean isUsable(Response r) { return r.isSuccessful(); }

Try / catch

try {
  // request + response handling
} catch (IOException e) {
  // Includes the 'Unexpected code' throw plus transport errors; inspect e.getMessage()
  if (e.getMessage() != null && e.getMessage().startsWith("Unexpected code")) {
    // HTTP-status originated from your own guard
  }
}

Prevention

When it happens

Trigger: GET to https://api.github.com/repos/lysine-dev/okhttp/issues returns a non-2xx code. Concretely: 404 when the repo 'lysine-dev/okhttp' does not exist or was renamed; 301/302 redirect to the canonical URL (OkHttp follows redirects by default, but a final non-2xx still triggers this); 403 when the unauthenticated GitHub rate limit (60 req/hour/IP) is exceeded; 451 for legal blocks.

Common situations: Repo was deleted or renamed; missing/wrong User-Agent header (GitHub requires one); hitting the low unauthenticated rate limit; copying this sample against a repo that does not exist; GitHub API versioning change returning 410 Gone.

Related errors


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

Appendix: source

Thrown at samples/guide/src/main/java/okhttp3/recipes/AccessHeaders.java:35

import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public final class AccessHeaders {
  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/repos/lysine-dev/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();

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

      System.out.println("Server: " + response.header("Server"));
      System.out.println("Date: " + response.header("Date"));
      System.out.println("Vary: " + response.headers("Vary"));
    }
  }

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

View on GitHub (pinned to 91a8b34c6f)