theonedev/onedev · warning · RuntimeException

java.lang.InterruptedException

Error message

java.lang.InterruptedException

What it means

Inside downloadLog's streaming loop the thread waits on the output stream (os.wait(5000)) for new log entries. If the thread is interrupted while waiting, the InterruptedException is caught and rethrown wrapped in a RuntimeException. This happens when the client disconnects and the server cancels the streaming request, or the request times out and the container interrupts the worker thread.

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/BuildLogStreamResource.java:99

					}
				}

			};
			logService.registerListener(logListener);
			sessionService.closeSession();
			try {
				var nextOffset = 0;
				LogSnippet snippet = logService.readLogSnippetReversely(loggingSupport, MAX_LOG_ENTRIES + 1);
				nextOffset = snippet.offset + snippet.entries.size();
				for (var entry : snippet.entries)
					writeEntry(os, entry);

				while (true) {
					synchronized (os) {
						try {
							os.wait(5000);
						} catch (InterruptedException e) {
							throw new RuntimeException(e);
						}
						var entries = logService.readLogEntries(loggingSupport, nextOffset, 0);
						if (!entries.isEmpty()) {
							nextOffset += entries.size();
							for (var entry : entries)
								writeEntry(os, entry);
						} else {
							var innerBuildStatus = sessionService.call(() -> buildService.load(buildId).getStatus());
							if (innerBuildStatus.isFinished()) {
								writeStatus(os, innerBuildStatus);
								break;
							} else {
								writeInt(os, 0);
								os.flush();
							}
						}
					}
				}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Increase client/proxy read timeouts so the stream is not cut during quiet periods (e.g. nginx proxy_read_timeout).
  2. Wrap the streaming call client-side with reconnect logic — re-request the log from the last known offset.
  3. If it occurs during shutdown, it is expected; treat InterruptedException-wrapping RuntimeException as stream termination, not data loss.
  4. For very long builds, poll the completed log after the build finishes instead of holding a stream open.

Example fix

// before: single long-lived stream
curl -f http://host/rest/builds/123/log -o build.log
// after: retry with backoff on stream termination
for i in 1 2 3; do
  curl -f --max-time 3600 http://host/rest/builds/123/log -o build.log && break
  sleep $((i * 5))
done
Defensive patterns

Strategy: retry

Try / catch

// retry streaming on interruption-caused termination
for (int attempt = 1; attempt <= 3; attempt++) {
    try {
        streamBuildLog(buildId, consumer);
        break;
    } catch (RuntimeException e) {
        if (e.getCause() instanceof InterruptedException || isDisconnect(e)) {
            sleep(attempt * 2000L);
            continue;
        }
        throw e;
    }
}

Prevention

When it happens

Trigger: Client closes the connection mid-stream (timeout, Ctrl-C on curl, proxy cutoff) causing the servlet container to interrupt the streaming thread; server shutdown during an active log stream; long-lived streams exceeding platform idle timeouts.

Common situations: curl or a browser with a short read timeout on a quiet build (no new log entries for >5s intervals then disconnect); reverse proxies (nginx) closing idle upstream connections; job cancels/aborts interrupting log followers.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/e3a3d7158a24d00b. Report an issue: GitHub.