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
- Read response.code() and response.body() before throwing to see the exact status and GitHub error message.
- Point the URL at a repo that actually exists, or use the generic 'repos/{owner}/{name}' path for your own repo.
- Add an OAuth/token Authorization header (or authenticator) to raise the GitHub rate limit if you are getting 403.
- Keep the custom User-Agent header shown in the sample; GitHub rejects requests without one.
- 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
- Never treat OkHttp Response objects as success without checking isSuccessful()/code().
- Add Authorization tokens for rate-limited APIs like GitHub.
- Always set a descriptive User-Agent for GitHub requests.
- Log response.code() and body before throwing so failures are diagnosable.
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)