{"record":{"id":"aa825a70705f8c72","repo":"CarGuo/GSYVideoPlayer","slug":"subtitle-url-is-empty","errorCode":null,"errorMessage":"Subtitle url is empty","messagePattern":"Subtitle url is empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"critical","filePath":"gsyVideoPlayer-java/src/main/java/com/shuyu/gsyvideoplayer/subtitle/GSYSubtitleLoader.java","lineNumber":90,"sourceCode":"    public void cancelCurrent() {\n        if (currentTask != null) {\n            currentTask.cancel(true);\n            currentTask = null;\n        }\n    }\n\n    public void release() {\n        cancelCurrent();\n        executorService.shutdownNow();\n    }\n\n    private String read(Context context, GSYSubtitleSource source) throws Exception {\n        InputStream inputStream = null;\n        HttpURLConnection connection = null;\n        try {\n            String url = source.getUrl();\n            if (TextUtils.isEmpty(url)) {\n                throw new IllegalArgumentException(\"Subtitle url is empty\");\n            }\n            Uri uri = Uri.parse(url);\n            String scheme = uri.getScheme();\n            if (\"http\".equalsIgnoreCase(scheme) || \"https\".equalsIgnoreCase(scheme)) {\n                connection = (HttpURLConnection) new URL(url).openConnection();\n                connection.setInstanceFollowRedirects(true);\n                connection.setConnectTimeout(15000);\n                connection.setReadTimeout(15000);\n                for (Map.Entry<String, String> entry : source.getHeaders().entrySet()) {\n                    connection.setRequestProperty(entry.getKey(), entry.getValue());\n                }\n                inputStream = new BufferedInputStream(connection.getInputStream());\n            } else if (\"file\".equalsIgnoreCase(scheme)) {\n                inputStream = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));\n            } else if (TextUtils.isEmpty(scheme)) {\n                inputStream = new BufferedInputStream(new FileInputStream(new File(url)));\n            } else {\n                inputStream = new BufferedInputStream(context.getContentResolver().openInputStream(uri));","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/CarGuo/GSYVideoPlayer/blob/e5d74d3aa9d7fb1393a879e33ee380f8f41354f1/gsyVideoPlayer-java/src/main/java/com/shuyu/gsyvideoplayer/subtitle/GSYSubtitleLoader.java#L72-L108","documentation":"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').","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (per-activity - leaks sockets)\npublic void play(String url) {\n    HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(this).build();\n    ...\n}\n\n// after - app-wide singleton\npublic class App extends Application {\n    private static HttpProxyCacheServer proxy;\n    public static HttpProxyCacheServer getProxy(Context ctx) {\n        if (proxy == null) proxy = new HttpProxyCacheServer.Builder(ctx).build();\n        return proxy;\n    }\n}","handlingStrategy":"fallback","validationCode":"if (proxy != null && proxy.isAlive()) { /* reuse */ } else { proxy = new HttpProxyCacheServer.Builder(app).build(); }","typeGuard":null,"tryCatchPattern":"try {\n    proxy = new HttpProxyCacheServer.Builder(app).build();\n} catch (IllegalStateException e) {\n    // transient socket/fd exhaustion: play direct url without proxy this session\n    playUrl(originalUrl); \n}","preventionTips":["One application-scoped HttpProxyCacheServer singleton, never per-activity","Release old instances before rebuilding","Monitor open fd count if you manage many player instances"],"tags":["proxy","server-socket","socket-exhaustion","singleton","android"],"backgroundTag":null,"analyzedSha":"e5d74d3aa9d7fb1393a879e33ee380f8f41354f1","analyzedAt":"2026-08-14T11:56:11.997Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}