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

failed to delete {file}

Error message

failed to delete {file}

What it means

IOException thrown by DiskLruCache.remove() when a clean file of the entry exists but File.delete() returns false. The cache keeps its size accounting consistent with the filesystem, so a file it cannot delete is treated as a hard failure rather than silently leaking the entry.

Source

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

	/**
	 * Drops the entry for {@code key} if it exists and can be removed. Entries
	 * actively being edited cannot be removed.
	 *
	 * @return true if an entry was removed.
	 */
	public synchronized boolean remove(String key) throws IOException {
		checkNotClosed();
		validateKey(key);
		Entry entry = lruEntries.get(key);
		if (entry == null || entry.currentEditor != null) {
			return false;
		}

		for (int i = 0; i < valueCount; i++) {
			File file = entry.getCleanFile(i);
			if (file.exists() && !file.delete()) {
				throw new IOException("failed to delete " + file);
			}
			size -= entry.lengths[i];
			fileCount--;
			entry.lengths[i] = 0;
		}

		redundantOpCount++;
		journalWriter.append(REMOVE + ' ' + key + '\n');
		lruEntries.remove(key);

		if (journalRebuildRequired()) {
			executorService.submit(cleanupCallable);
		}

		return true;
	}

	/** Returns true if this cache has been closed. */

View on GitHub (pinned to ba33ec64d0)

Solutions

  1. Close any open Snapshot streams (snapshot.close()) for the entry before calling remove().
  2. Verify the cache directory is writable and on healthy storage; prefer context.getCacheDir() (internal) over external storage.
  3. As a last resort, catch the IOException and wipe/rebuild the whole cache directory, since accounting is now untrustworthy.

Example fix

// before
Snapshot snap = cache.get(key);
InputStream is = snap.getInputStream(0);
// ... use is, never close ...
cache.remove(key); // may fail: file still open

// after
Snapshot snap = cache.get(key);
try (InputStream is = snap.getInputStream(0)) {
    // ... use is ...
} finally {
    snap.close();
}
cache.remove(key);
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure no open streams on the entry before removal
Snapshot snap = cache.get(key);
if (snap != null) snap.close(); // releases open InputStreams
cache.remove(key);

Try / catch

try {
    cache.remove(key);
} catch (IOException deleteFailed) {
    // size accounting is now suspect: rebuild the cache wholesale
    try { cache.delete(); } catch (IOException wipeFailed) { log(wipeFailed); }
    cache = reopenFreshCache(dir);
}

Prevention

When it happens

Trigger: cache.remove(key) while another process/stream still holds the file open (Windows or a still-open Snapshot InputStream), read-only filesystem or SELinux denial on the cache dir, or the file being deleted concurrently by an external cleanup.

Common situations: Android device with full/damaged emulated storage; a Snapshot's InputStream not closed before remove(); cache directory on external storage unmounted mid-operation; running on a desktop JVM against a locked file.

Related errors


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