square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Thrown in the PreemptiveAuth recipe: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response)`. A BasicAuthInterceptor unconditionally adds an Authorization header for requests to the configured host BEFORE the server challenges. If the credentials are wrong or the resource is gone, the server returns non-2xx and this throws. Because auth is preemptive, there is no 401->retry flow here.
Solutions
- Replace the sample username/password with real credentials.
- Confirm the secret path still exists (404 is not an auth failure).
- Log response.code() to distinguish 401/403 (auth) from 404 (missing).
- Make the host match in the interceptor match your actual target host(s).
Example fix
// before
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// after
if (response.code() == 401 || response.code() == 403) {
throw new IOException("Preemptive auth rejected (HTTP " + response.code() + ")");
}
if (!response.isSuccessful()) throw new IOException("HTTP " + response.code()); Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the secret path exists; verify credentials before relying on preemptive auth.
if (!validCredentials) throw new IllegalStateException("configure real credentials");
...
if (response.code() == 401 || response.code() == 403) { /* auth failure */ return; } Type guard
static boolean preemptiveAuthAccepted(Response r) { return r.code() != 401 && r.code() != 403; } Try / catch
try {
// call
} catch (IOException e) {
// includes 'Unexpected code' (HTTP status; often 401/403 for bad creds, 404 for missing path)
} Prevention
- Do not hardcode sample credentials; load real ones from config/secrets.
- Make the interceptor's host match your actual target host(s).
- Distinguish 401/403 (auth) from 404 (missing resource).
- Confirm the secret path still exists.
When it happens
Trigger: GET https://publicobject.com/secrets/hellosecret.txt with a preemptive Basic header returns 401/403 (wrong credentials), 404 (secret file removed), or 5xx. The interceptor matches only requests whose host equals 'publicobject.com', so a redirect to a different host would drop the header and likely yield 401.
Common situations: Using literal sample credentials 'jesse'/'password1' against a live server; the protected resource was removed; the server expects a scheme other than Basic; the host filter is too narrow/wide.
Related errors
AI-assisted analysis of square/okhttp@91a8b34c6f (2026-08-10).
Data as JSON: /api/errors/a69f9973cf4d3782.
Report an issue: GitHub.
Appendix: source
Thrown at samples/guide/src/main/java/okhttp3/recipes/PreemptiveAuth.java:41
import okhttp3.Response;
public final class PreemptiveAuth {
private final OkHttpClient client;
public PreemptiveAuth() {
client = new OkHttpClient.Builder()
.addInterceptor(
new BasicAuthInterceptor("publicobject.com", "jesse", "password1"))
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://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 PreemptiveAuth().run();
}
static final class BasicAuthInterceptor implements Interceptor {
private final String credentials;
private final String host;
BasicAuthInterceptor(String host, String username, String password) {
this.credentials = Credentials.basic(username, password);
this.host = host;
}
View on GitHub (pinned to 91a8b34c6f)