nostra13/Android-Universal-Image-Loader · error · IOException

LineReader is closed

Error message

LineReader is closed

What it means

StrictLineReader.readLine throws IOException('LineReader is closed') after close() has been called — the close path nulls the internal buffer, and subsequent reads detect it. This is a use-after-close lifecycle error, not data corruption, and surfaces during journal replay or writing in DiskLruCache when a reader is reused after being closed.

Source

Thrown at library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java:127

			if (buf != null) {
				buf = null;
				in.close();
			}
		}
	}

	/**
	 * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
	 * this end of line marker is not included in the result.
	 *
	 * @return the next line from the input.
	 * @throws IOException for underlying {@code InputStream} errors.
	 * @throws EOFException for the end of source stream.
	 */
	public String readLine() throws IOException {
		synchronized (in) {
			if (buf == null) {
				throw new IOException("LineReader is closed");
			}

			// Read more data if we are at the end of the buffered data.
			// Though it's an error to read after an exception, we will let {@code fillBuf()}
			// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
			if (pos >= end) {
				fillBuf();
			}
			// Try to find LF in the buffered data and return the line if successful.
			for (int i = pos; i != end; ++i) {
				if (buf[i] == LF) {
					int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
					String res = new String(buf, pos, lineEnd - pos, charset.name());
					pos = i + 1;
					return res;
				}
			}

View on GitHub (pinned to ba33ec64d0)

Solutions

  1. Scope reads strictly inside the reader's lifetime: read inside try, close in the single finally, and never touch the reader afterwards.
  2. On EOFException, break the loop immediately — do not read again after the stream ends.
  3. Restrict each StrictLineReader to one thread/one owner; no shared readers.

Example fix

// before
try {
    while (true) parse(reader.readLine());
} catch (EOFException e) {
    reader.close();
}
handle(reader.readLine()); // use after close

// after
try {
    while (true) parse(reader.readLine());
} catch (EOFException e) {
    // end of journal: stop reading
} finally {
    reader.close();
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    while (true) { line = reader.readLine(); process(line); }
} catch (EOFException done) {
    // normal end of stream — stop, do not read again
} finally {
    Util.closeQuietly(reader); // single close point
}

Prevention

When it happens

Trigger: Calling readLine() after close(); in-library, typically via an error path where the reader/stream was closed in a finally block but the read loop continued, or two threads sharing one StrictLineReader where one closes it. The internal readLine is synchronized on the underlying stream, so cross-thread close-after-read races are the realistic producer.

Common situations: Custom code built on StrictLineReader with mismatched try/finally scoping; concurrent close during parsing (e.g. shutdown thread closing the cache while open() is still replaying the journal); adapted code that catches EOFException, closes, then accidentally reads once more.

Related errors


AI-assisted analysis of nostra13/Android-Universal-Image-Loader@ba33ec64d0 (2026-08-14). Data as JSON: /api/errors/ea0394302ec8f387. Report an issue: GitHub.