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

unexpected journal header: [{magic}, {version}, {valueCountS

Error message

unexpected journal header: [{magic}, {version}, {valueCountString}, {blank}]

What it means

While opening an existing cache, DiskLruCache reads the journal file's five header lines and throws IOException if they don't exactly match the expected magic, version, appVersion, valueCount, and a blank line. The header ties the journal to a specific appVersion and valueCount, so any mismatch means the on-disk cache cannot be interpreted safely and is treated as corruption.

Source

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

		cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
		cache.rebuildJournal();
		return cache;
	}

	private void readJournal() throws IOException {
		StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
		try {
			String magic = reader.readLine();
			String version = reader.readLine();
			String appVersionString = reader.readLine();
			String valueCountString = reader.readLine();
			String blank = reader.readLine();
			if (!MAGIC.equals(magic)
					|| !VERSION_1.equals(version)
					|| !Integer.toString(appVersion).equals(appVersionString)
					|| !Integer.toString(valueCount).equals(valueCountString)
					|| !"".equals(blank)) {
				throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
						+ valueCountString + ", " + blank + "]");
			}

			int lineCount = 0;
			while (true) {
				try {
					readJournalLine(reader.readLine());
					lineCount++;
				} catch (EOFException endOfJournal) {
					break;
				}
			}
			redundantOpCount = lineCount - lruEntries.size();
		} finally {
			Util.closeQuietly(reader);
		}
	}

View on GitHub (pinned to ba33ec64d0)

Solutions

  1. Delete the cache directory contents (or call cache.delete() if it can still open) and let the cache rebuild an empty journal.
  2. Keep the appVersion passed to DiskLruCache.open() stable unless you intend to invalidate the cache.
  3. Give each distinct cache configuration its own subdirectory so journals never mix.

Example fix

// before
// appVersion changed between releases -> header mismatch on old journal
DiskLruCache cache = DiskLruCache.open(dir, 2, 1, maxSize, maxFileCount);

// after
try {
    cache = DiskLruCache.open(dir, 2, 1, maxSize, maxFileCount);
} catch (IOException e) {
    Util.deleteContents(dir); // journal unusable: wipe and rebuild
    cache = DiskLruCache.open(dir, 2, 1, maxSize, maxFileCount);
}
Defensive patterns

Strategy: fallback

Try / catch

DiskLruCache cache;
try {
    cache = DiskLruCache.open(dir, appVersion, valueCount, maxSize, maxFileCount);
} catch (IOException headerMismatch) {
    // journal unusable (appVersion/valueCount change or corruption): wipe and rebuild
    Util.deleteContents(dir);
    try {
        cache = DiskLruCache.open(dir, appVersion, valueCount, maxSize, maxFileCount);
    } catch (IOException retryFailed) {
        throw new IllegalStateException("cannot rebuild disk cache at " + dir, retryFailed);
    }
}

Prevention

When it happens

Trigger: DiskLruCache.open() on a directory whose journal was written by a different appVersion (e.g. you bumped the version code passed to open()), a different valueCount, a newer/older journal format, or a truncated/garbled journal file (partial write, device power loss, file synced from another cache).

Common situations: Shipping an app update that changes the appVersion argument; changing diskCache(... ) configuration (valueCount) while reusing the same cache directory; journal corruption after unclean shutdown; pointing two cache configurations at one directory.

Related errors


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