{"record":{"id":"36e443affddf625e","repo":"theonedev/onedev","slug":"java-io-ioexception","errorCode":null,"errorMessage":"java.io.IOException","messagePattern":"java\\.io\\.IOException","errorType":"exception","errorClass":"RuntimeException","httpStatus":500,"severity":"warning","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/BuildLogStreamResource.java","lineNumber":129,"sourceCode":"\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\twriteInt(os, 0);\n\t\t\t\t\t\t\t\tos.flush();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tsessionService.openSession();\n\t\t\t\tlogService.deregisterListener(logListener);\n\t\t\t}\n\t\t};\n\t}\n\t\n\tprivate void writeInt(OutputStream os, int value) {\n\t\ttry {\n\t\t\tos.write(ByteBuffer.allocate(Integer.BYTES).putInt(value).array());\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\tprivate void writeStatus(OutputStream os, Status status) {\n\t\ttry {\n\t\t\twriteInt(os, status.name().length() * -1);\n\t\t\tos.write(status.name().getBytes(UTF_8));\n\t\t\tos.flush();\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\tprivate void writeEntry(OutputStream os, JobLogEntryEx entry) {\n\t\ttry {\n\t\t\tvar bytes = objectMapper.writeValueAsBytes(entry.transformEmojis());\n\t\t\twriteInt(os, bytes.length);\n\t\t\tos.write(bytes);","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/BuildLogStreamResource.java#L111-L147","documentation":"writeInt writes a 4-byte integer length prefix to the log stream's OutputStream. If the underlying connection is broken (client gone, socket closed) os.write throws IOException, which is wrapped in a RuntimeException. This is the streaming framework's way of aborting when the consumer disconnects.","triggerScenarios":"Client disconnects while downloadLog is streaming; socket reset by load balancer; connection pool closed; any failure writing the binary length prefix for a status or log entry.","commonSituations":"User cancels the download mid-stream; proxy idle cutoff during slow builds; mobile/VPN clients dropping; browser tab closed while tailing a log.","solutions":["Treat this as normal stream termination when the client intentionally disconnects — catch and ignore on the server or abort the stream.","Client side: keep the connection alive (disable idle timeouts, use keep-alive) for long builds.","Retry the log download from scratch or from a known offset if the stream drops.","Check intermediary proxies/firewalls for connection idle limits and raise them."],"exampleFix":"// server-side view: RuntimeException wrapping means broken pipe\ntry {\n    streamLog(os);\n} catch (RuntimeException e) {\n    if (e.getCause() instanceof IOException) {\n        log.info(\"client disconnected while streaming build log\");\n        return; // normal termination\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    consumeLogStream(in);\n} catch (RuntimeException e) {\n    Throwable cause = e.getCause();\n    if (cause instanceof IOException) {\n      log.warn(\"Log stream broken (client disconnect or network issue): \" + cause.getMessage());\n    } else throw e;\n}","preventionTips":["Keep clients alive through quiet periods (timeouts > server wait interval of 5s).","Disable aggressive proxy buffering/timeout for streaming routes.","Treat RuntimeException(IOException) during streaming as expected termination.","Avoid closing the connection abruptly mid-download from client code."],"tags":["io","streaming","network"],"backgroundTag":"broken-pipe","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}