square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Recipe-level guard after POSTing a pipe-backed RequestBody to GitHub's markdown/raw endpoint. A separate 'writer' thread feeds the pipe (Okio Pipe of 8192 bytes) while the request thread reads from it in writeTo(). If the server's final HTTP response is non-2xx, java.io.IOException("Unexpected code " + response) is thrown. If the writer thread dies (IOException/InterruptedException in streamPrimesToSinkAsynchronously) the pipe stalls and execute() throws a different IOException, not this line.
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 4fc0831380)
Solutions
- Ensure the writer thread always closes the sink in a finally block so a stalled pipe cannot hang execute().
- Inspect response.code() when you do reach this line; treat as a normal HTTP error.
- Authenticate to GitHub and verify the content-type.
Example fix
// before (writer thread)
try {
... sink.writeUtf8(...); Thread.sleep(10); ...
sink.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
// after
try {
... sink.writeUtf8(...); ...
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} finally {
try { sink.close(); } catch (IOException ignored) {}
}
// and at the response guard:
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("GitHub " + response.code() + " after streamed upload");
}
...
} Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the writer thread always closes the sink so a stalled pipe cannot hang execute()
Thread writer = new Thread(() -> {
try (BufferedSink s = sink) { // try-with-resources guarantees close
// ...write payload...
} catch (IOException e) {
log.warn("writer failed", e);
}
}, "writer");
writer.setDaemon(true);
writer.start(); Try / catch
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new HttpException(response.code(), response.message());
}
} catch (IOException e) {
// a crashed/unclosed writer thread surfaces here as a pipe stall / connection reset
if (writerThread.getState() == Thread.State.TERMINATED) {
throw new UploadStallException("writer thread died before closing pipe", e);
}
throw e;
} Prevention
- Always close the Okio sink in a finally / try-with-resources on the writer thread.
- Make the writer thread a daemon so JVM exit is not blocked by a stalled pipe.
- Handle InterruptedException by restoring the interrupt status and closing the sink.
- Distinguish a writer-thread crash (transport IOException) from an HTTP-error guard.
When it happens
Trigger: POST https://api.github.com/markdown/raw with a PipeBody while a writer thread calls sink.writeUtf8(...)+sink.close(). Reaches this line when both threads cooperated and the upload completed but GitHub returned 4xx/5xx (403 rate limit, 415 media type). A writer-thread crash surfaces as a stuck pipe / connection-reset IOException earlier.
Common situations: Writer thread hits InterruptedException (Thread.sleep interrupted) and prints stack trace but does not close the sink cleanly → the request never completes and execute() blocks/fails instead of reaching this line; GitHub rate limiting; mismatched content-type.
Related errors
AI-assisted analysis of square/okhttp@4fc0831380 (2026-08-04).
Data as JSON: /data/errors/7711b1336a0c6ec6.json.
Report an issue: GitHub.