CarGuo/GSYVideoPlayer · error · IllegalStateException

Exo cache folder is locked: ${cachePath}

Error message

Exo cache folder is locked: ${cachePath}

What it means

ExoPlayer's SimpleCache locks its cache folder with a lock file so only one instance can own it at a time. ExoSourceManager.getOrCreateCacheHolder checks SimpleCache.isCacheFolderLocked before creating a new cache; when the folder is already owned by another SimpleCache instance (typically in another process or a not-yet-released instance) and fallbackWhenLocked is false, it throws IllegalStateException. This protects against the classic 'Another SimpleCache instance uses the folder' crash.

Source

Thrown at gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/ExoSourceManager.java:467

        CacheHolder cacheHolder = getOrCreateCacheHolder(context, cacheDir, true, true);
        if (cacheHolder == null) {
            return null;
        }
        mCurrentCachePath = cacheHolder.cachePath;
        return cacheHolder;
    }

    private static synchronized CacheHolder getOrCreateCacheHolder(Context context, File cacheDir, boolean incrementRef, boolean fallbackWhenLocked) {
        String cachePath = buildCachePath(context, cacheDir);
        CacheHolder cacheHolder = sCacheHolderMap.get(cachePath);
        if (cacheHolder == null) {
            File cacheFolder = new File(cachePath);
            if (SimpleCache.isCacheFolderLocked(cacheFolder)) {
                if (fallbackWhenLocked) {
                    Log.w(TAG, "Exo cache folder is locked, fallback without cache: " + cachePath);
                    return null;
                }
                throw new IllegalStateException("Exo cache folder is locked: " + cachePath);
            }
            Cache cache = new SimpleCache(cacheFolder, new LeastRecentlyUsedCacheEvictor(sCacheMaxSize),
                sDatabaseProvider != null ? sDatabaseProvider : new StandaloneDatabaseProvider(context));
            cacheHolder = new CacheHolder(cachePath, cache);
            sCacheHolderMap.put(cachePath, cacheHolder);
        }
        if (incrementRef) {
            cacheHolder.refCount++;
        }
        return cacheHolder;
    }

    private static synchronized void releaseCacheHolder(String cachePath) {
        CacheHolder cacheHolder = sCacheHolderMap.get(cachePath);
        if (cacheHolder == null) {
            return;
        }
        if (cacheHolder.refCount > 0) {

View on GitHub (pinned to e5d74d3aa9)

Solutions

  1. Use the fallback overload acquireCacheSingleInstance(context, cacheDir, true) so playback continues without cache when the folder is locked
  2. Ensure ExoSourceManager.releaseAllCacheHolder(context) is called in the player release path so the lock is freed before a new instance is created
  3. Give each process its own cache directory (append process name to the cache dir) so two SimpleCache instances never share a folder
  4. If the lock is stale after a crash, delete the lock file (exoplayer_cache_lock / .lock inside the cache dir) on app start before creating the cache

Example fix

// before
Cache cache = ExoSourceManager.acquireCacheSingleInstance(context, cacheDir, false);

// after - degrade to cache-less playback instead of crashing
Cache cache = ExoSourceManager.acquireCacheSingleInstance(context, cacheDir, true);
if (cache == null) {
    // proceed without cache (direct HttpDataSource)
}
Defensive patterns

Strategy: fallback

Validate before calling

File cacheDir = new File(context.getCacheDir(), "exo");
if (androidx.media3.database.SimpleCache.isCacheFolderLocked(cacheDir)) {
    // either release previous holders or pass fallbackWhenLocked=true
}

Try / catch

try {
    cache = ExoSourceManager.acquireCacheSingleInstance(ctx, cacheDir, false);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("locked")) {
        cache = ExoSourceManager.acquireCacheSingleInstance(ctx, cacheDir, true); // null = play without cache
    } else throw e;
}

Prevention

When it happens

Trigger: Calling ExoSourceManager.acquireCacheSingleInstance(context, cacheDir, false) (or any path with fallbackWhenLocked=false) while the cache folder is locked: e.g. the app process died without releasing the cache, a :remote or multi-process setup has another SimpleCache on the same folder, or the previous player instance's cache was never released via ExoSourceManager.releaseAllCacheHolder.

Common situations: Multi-process apps (webview/player in separate process) sharing one cache dir; app crash leaving a stale lock file; calling Media3CacheExportUtils or CacheHelper before the previous cache holder was released; enabling ExoPlayer caching after the process was killed mid-playback.

Related errors


AI-assisted analysis of CarGuo/GSYVideoPlayer@e5d74d3aa9 (2026-08-14). Data as JSON: /api/errors/ca96c95f73ce52a4. Report an issue: GitHub.