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

  1. Replace the sample username/password with real credentials.
  2. Confirm the secret path still exists (404 is not an auth failure).
  3. Log response.code() to distinguish 401/403 (auth) from 404 (missing).
  4. 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

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)