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

not a readable directory: {dir}

Error message

not a readable directory: {dir}

What it means

Thrown by Util.deleteContents (a helper ported from OkHttp's DiskLruCache) when File.listFiles() returns null, meaning the path is not a directory or the process lacks read permission on it. In this library it is called from DiskLruCache/DiskLruCacheUtil when the LruDiskCache initializes and clears its cache directory before use. The IOException propagates up through LruDiskCache construction, aborting disk cache creation.

Source

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

			char[] buffer = new char[1024];
			int count;
			while ((count = reader.read(buffer)) != -1) {
				writer.write(buffer, 0, count);
			}
			return writer.toString();
		} finally {
			reader.close();
		}
	}

	/**
	 * Deletes the contents of {@code dir}. Throws an IOException if any file
	 * could not be deleted, or if {@code dir} is not a readable directory.
	 */
	static void deleteContents(File dir) throws IOException {
		File[] files = dir.listFiles();
		if (files == null) {
			throw new IOException("not a readable directory: " + dir);
		}
		for (File file : files) {
			if (file.isDirectory()) {
				deleteContents(file);
			}
			if (!file.delete()) {
				throw new IOException("failed to delete file: " + file);
			}
		}
	}

	static void closeQuietly(/*Auto*/Closeable closeable) {
		if (closeable != null) {
			try {
				closeable.close();
			} catch (RuntimeException rethrown) {
				throw rethrown;
			} catch (Exception ignored) {

View on GitHub (pinned to ba33ec64d0)

Solutions

  1. Verify the cache path with context.getCacheDir() (always app-writable) instead of external storage
  2. Ensure the File passed to diskCacheFileNameGenerator/diskCacheFile(...) is created as a directory (mkdirs()) before building the configuration
  3. Add WRITE_EXTERNAL_STORAGE permission if caching on external storage pre-API 19
  4. Wrap ImageLoader.getInstance().init(...) in try/catch (IOException/IllegalStateException) and fall back to a noDiskCache configuration if cache setup fails

Example fix

// before
File cacheDir = new File(context.getCacheDir(), "ui/images");
// ... later, without ever creating it:
config.diskCache(new LruDiskCache(cacheDir, ...)); // may throw: not a readable directory

// after
File cacheDir = new File(context.getCacheDir(), "ui/images");
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
    cacheDir = context.getCacheDir(); // safe fallback
}
config.diskCache(new LruDiskCache(cacheDir, BaseMd5FileNameGenerator.INSTANCE, 50 * 1024 * 1024, 0));
Defensive patterns

Strategy: validation

Validate before calling

File cacheDir = new File(context.getCacheDir(), "uil");
boolean usable = cacheDir.isDirectory() || cacheDir.mkdirs();
if (usable) {
    File[] probe = cacheDir.listFiles();
    usable = probe != null; // listFiles() null == not readable
}
if (usable) config.diskCache(new LruDiskCache(cacheDir, BaseMd5FileNameGenerator.INSTANCE, CACHE_SIZE, 0));
// else: omit disk cache from configuration

Try / catch

try {
    ImageLoader.getInstance().init(configWithDiskCache);
} catch (IOException e) {
    // fall back to memory-cache-only config
    ImageLoader.getInstance().init(memoryOnlyConfig);
}

Prevention

When it happens

Trigger: Configuring ImageLoaderConfiguration with diskCache(...) (LruDiskCache/DiskLruCache) where the supplied cache File points to a regular file instead of a directory, a directory with no read permission (chmod 000, restrictive SELinux), or a path that was deleted concurrently between the existence check and listFiles().

Common situations: Passing getCacheDir()+"/subdir" as a String instead of creating the directory first; a previous run crashed mid-directory creation; device storage mounted read-only; on some devices /sdcard access without WRITE_EXTERNAL_STORAGE permission; multi-process apps where another process deleted the cache dir.

Related errors


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