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

  1. Confirm the credentials are actually correct for the realm — check response.challenges() (printed by the recipe) for the expected scheme/realm.
  2. Inspect response.code() and the Authorization history via response.priorResponse() to see how many auth attempts occurred.
  3. Match the Authenticator's scheme to what the server advertises (use Credentials.basic only for WWW-Authenticate: Basic).
  4. Use https:// so credentials are encrypted.
  5. 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

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.