square/okhttp · error · IOException

Unexpected code

Error message

Unexpected code 

What it means

Thrown after the configured Authenticator runs: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response)`. The authenticator adds Basic credentials when the server issues a 401 challenge. If auth succeeds the final response is 2xx; if it fails (wrong credentials, the server still returns 401/403), response.isSuccessful() is false and this throws. The authenticator itself bails out (returns null) if it already tried once.

Solutions

  1. Replace "jesse"/"password1" with valid credentials for the resource you are actually hitting.
  2. Log response.code() and response.challenges() (as the sample already does) to see the auth scheme and status.
  3. Confirm the protected path still exists; 404 is not an auth failure.
  4. If the server uses a non-Basic scheme, implement an Authenticator that handles that scheme or returns null to stop.

Example fix

// before
String credential = Credentials.basic("jesse", "password1");
...
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

// after
String credential = Credentials.basic(realUser, realPassword);
...
if (response.code() == 401 || response.code() == 403) {
  throw new IOException("Authentication failed (" + response.code() + ")");
}
if (!response.isSuccessful()) throw new IOException("HTTP " + response.code());
Defensive patterns

Strategy: try-catch

Validate before calling

// Inspect challenges + code before trusting the response.
if (response.code() == 401) {
  List<Challenge> challenges = response.challenges();
  // If Basic is not among them, the authenticator cannot help.
}

Type guard

static boolean authenticated(Response r) { return r.code() != 401 && r.code() != 403; }

Try / catch

try {
  // call
} catch (IOException e) {
  if (e.getMessage().contains("Unexpected code")) {
    // auth or status failure from the recipe guard; re-auth or surface to user
  }
}

Prevention

When it happens

Trigger: Credentials.basic("jesse", "password1") do not match the protected resource, so publicobject.com reponds 401 a second time; the resource path /secrets/hellosecret.txt was removed and returns 404; the server uses a non-Basic scheme the authenticator does not satisfy; the host no longer requires auth and returns a redirect.

Common situations: Using the literal sample credentials against a live server; the secret file was deleted; the server switched to digest/OAuth; copy-pasting the sample without changing username/password to real values.

Related errors


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

Appendix: 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 91a8b34c6f)