{"record":{"id":"8b8fa95fe8be9bb5","repo":"apache/cordova-android","slug":"tried-to-perform-an-io-operation-on-the-webcore-th","errorCode":null,"errorMessage":"Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.","messagePattern":"Tried to perform an IO operation on the WebCore thread\\. Use CordovaInterface\\.getThreadPool\\(\\) instead\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"framework/src/org/apache/cordova/CordovaResourceApi.java","lineNumber":425,"sourceCode":"    }\n\n    public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException {\n        copyResource(openForRead(sourceUri), outputStream);\n    }\n\n    // Added in 3.5.0.\n    public void copyResource(Uri sourceUri, Uri dstUri) throws IOException {\n        copyResource(openForRead(sourceUri), openOutputStream(dstUri));\n    }\n\n    private void assertBackgroundThread() {\n        if (threadCheckingEnabled) {\n            Thread curThread = Thread.currentThread();\n            if (curThread == Looper.getMainLooper().getThread()) {\n                throw new IllegalStateException(\"Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead.\");\n            }\n            if (curThread == jsThread) {\n                throw new IllegalStateException(\"Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.\");\n            }\n        }\n    }\n\n    private String getDataUriMimeType(Uri uri) {\n        String uriAsString = uri.getSchemeSpecificPart();\n        int commaPos = uriAsString.indexOf(',');\n        if (commaPos == -1) {\n            return null;\n        }\n        String[] mimeParts = uriAsString.substring(0, commaPos).split(\";\");\n        if (mimeParts.length > 0) {\n            return mimeParts[0];\n        }\n        return null;\n    }\n\n    private OpenForReadResult readDataUri(Uri uri) {","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/apache/cordova-android/blob/7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3/framework/src/org/apache/cordova/CordovaResourceApi.java#L407-L443","documentation":"The second branch of assertBackgroundThread: if the calling thread is the WebView's JavaScript (WebCore) bridge thread — recorded by CordovaBridge as CordovaResourceApi.jsThread — IO calls throw IllegalStateException. Plugin execute() actions run on this JS thread by default, and blocking it stalls every subsequent JS->native call.","triggerScenarios":"Doing IO directly inside CordovaPlugin.execute() (which runs on the JS bridge thread) without dispatching to the thread pool: resourceApi.openForRead(...), copyResource(...), createHttpConnection(...) called synchronously in the action handler.","commonSituations":"Plugin performs file reads/writes or HTTP requests in execute() directly; upgrading an old plugin written before thread checking (added in 3.5.0) and hitting the now-enforced rule; intermittent because some code paths dispatch and others do not.","solutions":["Dispatch all IO from execute() to the pool: cordova.getThreadPool().execute(() -> { ...IO...; callbackContext.success(...); })","Keep only argument parsing/sync checks in execute() itself","Never disable thread checking in production to silence this; it exists to prevent deadlocking the JS bridge"],"exampleFix":"// before: runs on the JS bridge thread -> IllegalStateException\npublic boolean execute(String action, JSONArray args, CallbackContext cb) {\n    resourceApi.copyResource(src, dst);\n    return true;\n}\n\n// after\npublic boolean execute(String action, JSONArray args, CallbackContext cb) {\n    cordova.getThreadPool().execute(() -> {\n        try { resourceApi.copyResource(src, dst); cb.success(); }\n        catch (IOException e) { cb.error(e.getMessage()); }\n    });\n    return true;\n}","handlingStrategy":"validation","validationCode":"// in plugin execute(): never do IO inline; always dispatch\npublic boolean execute(String action, JSONArray args, CallbackContext cb) {\n    cordova.getThreadPool().execute(() -> doIo(action, args, cb));\n    return true;\n}","typeGuard":"static boolean isSafeIoThread() {\n    Thread t = Thread.currentThread();\n    return t != Looper.getMainLooper().getThread() && t != CordovaResourceApi.jsThread;\n}","tryCatchPattern":"if (isSafeIoThread()) { doIo(cb); } else { cordova.getThreadPool().execute(() -> doIo(cb)); }","preventionTips":["Treat execute() as a dispatcher, not a worker","Never block the JS bridge thread with file or network IO","Do not disable thread checking to hide this error"],"tags":["cordova","cordova-android","java","threading","js-thread","resource-api"],"backgroundTag":"io-on-main-thread","analyzedSha":"7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3","analyzedAt":"2026-08-22T04:57:58.868Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}