square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Recipe-level guard after an authenticated GET. The client is configured with an Authenticator that retries once with HTTP Basic credentials (Credentials.basic("jesse","password1")) on a 401. After the authenticator gives up (it returns null if an Authorization header is already present), the final Response reaches this line; if it is still non-2xx, java.io.IOException("Unexpected code " + response) is thrown.
Source
Thrown at samples/guide/src/main/java/okhttp3/recipes/Authenticate.java:50
}
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
})
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.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 Authenticate().run();
}
}
View on GitHub (pinned to 4fc0831380)
Solutions
- Confirm the credentials are actually correct for the realm — check response.challenges() (printed by the recipe) for the expected scheme/realm.
- Inspect response.code() and the Authorization history via response.priorResponse() to see how many auth attempts occurred.
- Match the Authenticator's scheme to what the server advertises (use Credentials.basic only for WWW-Authenticate: Basic).
- Use https:// so credentials are encrypted.
- If 404 after auth, the path is wrong — nothing to do with authentication.
Example fix
// before
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
...
}
// after
try (Response response = client.newCall(request).execute()) {
if (response.code() == 401) {
throw new IOException("Auth failed; challenges=" + response.challenges()
+ " (check username/password and scheme)");
}
if (!response.isSuccessful()) {
throw new IOException("HTTP " + response.code() + " for " + response.request().url());
}
...
} Defensive patterns
Strategy: validation
Validate before calling
// Match the auth scheme the server actually advertises
try (Response probe = clientWithoutAuth.newCall(probeRequest).execute()) {
List<Challenge> challenges = probe.challenges(); // scheme + realm from WWW-Authenticate
if (challenges.isEmpty() || !"basic".equalsIgnoreCase(challenges.get(0).scheme())) {
throw new IllegalStateException("Server does not accept Basic auth: " + challenges);
}
} Try / catch
try (Response response = client.newCall(request).execute()) {
if (response.code() == 401) {
// Auth failed even after the Authenticator retried once
throw new AuthException(response.challenges());
}
if (!response.isSuccessful()) throw new HttpException(response.code(), response.message());
// use response
} Prevention
- Confirm the credentials and realm before deploying.
- Inspect response.challenges() to verify the scheme is Basic.
- Use https:// so Basic credentials are encrypted.
- Limit the Authenticator to one retry to avoid loops (the recipe returns null when Authorization is already set).
When it happens
Trigger: GET http://publicobject.com/secrets/hellosecret.txt where the server demands auth. Thrown when the Basic credentials are wrong/expired (final response stays 401), when the authenticator returns null on the second 401, or when the protected resource genuinely returns 403/404 after authenticating. Note the URL uses http, so credentials are sent in cleartext after the redirect.
Common situations: Placeholder credentials 'jesse/password1' from the recipe were never valid for your target; server changed its auth scheme (Digest, OAuth, Bearer) so Basic is rejected; the resource was moved/deleted (404 after auth); using http:// so the credentials leak on the wire and the host redirects to https mid-flow.
Related errors
AI-assisted analysis of square/okhttp@4fc0831380 (2026-08-04).
Data as JSON: /data/errors/86751ab560a219f0.json.
Report an issue: GitHub.