CarGuo/GSYVideoPlayer · critical · IllegalArgumentException
Subtitle url is empty
Error message
Subtitle url is empty
What it means
HttpProxyCacheServer's constructor binds a ServerSocket to the loopback proxy host with a OS-assigned port, installs an IgnoreHostProxySelector, and starts a wait-connections thread gated by a CountDownLatch. If binding/opening fails (IOException) or the start signal is interrupted before the server thread comes up, it shuts down the socket processor and throws IllegalStateException('Error starting local proxy server').
Solutions
- Keep a single application-scoped HttpProxyCacheServer singleton (static or in Application) and reuse it
- Ensure the old instance is fully released/unregistered before creating another
- Retry construction once after a brief delay - transient fd exhaustion often clears
- If a VPN/adaptive-network tool blocks loopback, test on another device and report incompatibility
Example fix
// before (per-activity - leaks sockets)
public void play(String url) {
HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(this).build();
...
}
// after - app-wide singleton
public class App extends Application {
private static HttpProxyCacheServer proxy;
public static HttpProxyCacheServer getProxy(Context ctx) {
if (proxy == null) proxy = new HttpProxyCacheServer.Builder(ctx).build();
return proxy;
}
} Defensive patterns
Strategy: fallback
Validate before calling
if (proxy != null && proxy.isAlive()) { /* reuse */ } else { proxy = new HttpProxyCacheServer.Builder(app).build(); } Try / catch
try {
proxy = new HttpProxyCacheServer.Builder(app).build();
} catch (IllegalStateException e) {
// transient socket/fd exhaustion: play direct url without proxy this session
playUrl(originalUrl);
} Prevention
- One application-scoped HttpProxyCacheServer singleton, never per-activity
- Release old instances before rebuilding
- Monitor open fd count if you manage many player instances
When it happens
Trigger: Constructing new HttpProxyCacheServer.Builder(context).build() when the loopback interface is unavailable, the process hit its file-descriptor/socket limit, or the waitConnectionThread was interrupted during startup (startSignal.await threw InterruptedException).
Common situations: Creating a new HttpProxyCacheServer per video/fragment instead of keeping one app-wide singleton (socket exhaustion); devices with networking disabled/airplane-mode edge cases affecting InetAddress.getByName(PROXY_HOST); heavy fd usage from leaked players; some emulators/VPN configurations interfering with loopback binding.
Related errors
- Error recreate zero-size file
- Exo cache folder is locked
- ExoPlayer Cache 未初始化,请先播放视频
- Max count must be positive number!
- Max size must be positive number!
AI-assisted analysis of CarGuo/GSYVideoPlayer@e5d74d3aa9 (2026-08-14).
Data as JSON: /api/errors/aa825a70705f8c72.
Report an issue: GitHub.
Appendix: source
Thrown at gsyVideoPlayer-java/src/main/java/com/shuyu/gsyvideoplayer/subtitle/GSYSubtitleLoader.java:90
public void cancelCurrent() {
if (currentTask != null) {
currentTask.cancel(true);
currentTask = null;
}
}
public void release() {
cancelCurrent();
executorService.shutdownNow();
}
private String read(Context context, GSYSubtitleSource source) throws Exception {
InputStream inputStream = null;
HttpURLConnection connection = null;
try {
String url = source.getUrl();
if (TextUtils.isEmpty(url)) {
throw new IllegalArgumentException("Subtitle url is empty");
}
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setInstanceFollowRedirects(true);
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
for (Map.Entry<String, String> entry : source.getHeaders().entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
inputStream = new BufferedInputStream(connection.getInputStream());
} else if ("file".equalsIgnoreCase(scheme)) {
inputStream = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));
} else if (TextUtils.isEmpty(scheme)) {
inputStream = new BufferedInputStream(new FileInputStream(new File(url)));
} else {
inputStream = new BufferedInputStream(context.getContentResolver().openInputStream(uri));View on GitHub (pinned to e5d74d3aa9)