square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Thrown in the PostStreamingWithPipe recipe: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response)`. It uses an okio Pipe so a separate writer thread streams a prime table to the request body while the main thread awaits the response. The guard fires on a non-2xx final status. A writer-thread failure (caught and printStackTrace'd) leaves the pipe unclosed, which can cause the request to hang or fail earlier as a transport error rather than reaching this line.
Solutions
- Ensure the writer thread always closes the sink in a finally block so the body terminates cleanly.
- Surface writer-thread exceptions to the caller rather than only printStackTrace, so transport vs HTTP errors are distinguishable.
- Check response.code() when you do reach this line.
- Confirm markdown/raw is still supported; switch endpoints if GitHub changed it.
Example fix
// before
// writer thread:
try { ... sink.writeUtf8(...); sink.close(); }
catch (IOException | InterruptedException e) { e.printStackTrace(); }
...
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// after
// writer thread:
try { ... sink.writeUtf8(...); }
catch (IOException | InterruptedException e) { writeError = e; }
finally { sink.close(); }
...
if (writeError != null) throw new IOException("writer failed", writeError);
if (!response.isSuccessful()) throw new IOException("HTTP " + response.code()); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the writer thread always closes the sink; surface its errors.
// Then branch on status only if the writer succeeded.
if (writerError != null) throw new IOException("writer failed", writerError);
if (!response.isSuccessful()) { /* code + body */ return; } Type guard
static boolean bodyComplete(Throwable writerError) { return writerError == null; } Try / catch
try {
// call with piped body
} catch (IOException e) {
// could be writer-thread failure, pipe deadlock, or 'Unexpected code' HTTP status
// inspect cause chain
} Prevention
- Always close the pipe sink in a finally block so the body terminates.
- Surface writer-thread exceptions to the caller; do not just printStackTrace.
- Size the okio Pipe buffer to avoid deadlock between writer and network.
- Distinguish writer/transport failures from HTTP-status failures.
When it happens
Trigger: POST https://api.github.com/markdown/raw returns 404/403/400/5xx after the writer thread finished. Alternatively the writer thread throws (e.g. InterruptedException) and never closes the sink, so the call may time out as an IOException before this status check runs.
Common situations: GitHub deprecating markdown/raw; rate limit; the writer thread dying silently (its exception is only printed) so the body is incomplete and GitHub returns 400; pipe deadlock if the buffer size is wrong.
Related errors
AI-assisted analysis of square/okhttp@91a8b34c6f (2026-08-10).
Data as JSON: /api/errors/7711b1336a0c6ec6.
Report an issue: GitHub.
Appendix: source
Thrown at samples/guide/src/main/java/okhttp3/recipes/PostStreamingWithPipe.java:45
public final class PostStreamingWithPipe {
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.get("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
final PipeBody pipeBody = new PipeBody();
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(pipeBody)
.build();
streamPrimesToSinkAsynchronously(pipeBody.sink());
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
}
private void streamPrimesToSinkAsynchronously(final BufferedSink sink) {
Thread thread = new Thread("writer") {
@Override public void run() {
try {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
System.out.println(i);
Thread.sleep(10);
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
sink.close();
} catch (IOException | InterruptedException e) {View on GitHub (pinned to 91a8b34c6f)