apache/cordova-android · error · IllegalStateException
Tried to perform an IO operation on the WebCore thread. Use
Error message
Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.
What it means
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.
Source
Thrown at framework/src/org/apache/cordova/CordovaResourceApi.java:425
}
public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException {
copyResource(openForRead(sourceUri), outputStream);
}
// Added in 3.5.0.
public void copyResource(Uri sourceUri, Uri dstUri) throws IOException {
copyResource(openForRead(sourceUri), openOutputStream(dstUri));
}
private void assertBackgroundThread() {
if (threadCheckingEnabled) {
Thread curThread = Thread.currentThread();
if (curThread == Looper.getMainLooper().getThread()) {
throw new IllegalStateException("Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead.");
}
if (curThread == jsThread) {
throw new IllegalStateException("Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead.");
}
}
}
private String getDataUriMimeType(Uri uri) {
String uriAsString = uri.getSchemeSpecificPart();
int commaPos = uriAsString.indexOf(',');
if (commaPos == -1) {
return null;
}
String[] mimeParts = uriAsString.substring(0, commaPos).split(";");
if (mimeParts.length > 0) {
return mimeParts[0];
}
return null;
}
private OpenForReadResult readDataUri(Uri uri) {View on GitHub (pinned to 7c1e190064)
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
Example fix
// before: runs on the JS bridge thread -> IllegalStateException
public boolean execute(String action, JSONArray args, CallbackContext cb) {
resourceApi.copyResource(src, dst);
return true;
}
// after
public boolean execute(String action, JSONArray args, CallbackContext cb) {
cordova.getThreadPool().execute(() -> {
try { resourceApi.copyResource(src, dst); cb.success(); }
catch (IOException e) { cb.error(e.getMessage()); }
});
return true;
} Defensive patterns
Strategy: validation
Validate before calling
// in plugin execute(): never do IO inline; always dispatch
public boolean execute(String action, JSONArray args, CallbackContext cb) {
cordova.getThreadPool().execute(() -> doIo(action, args, cb));
return true;
} Type guard
static boolean isSafeIoThread() {
Thread t = Thread.currentThread();
return t != Looper.getMainLooper().getThread() && t != CordovaResourceApi.jsThread;
} Try / catch
if (isSafeIoThread()) { doIo(cb); } else { cordova.getThreadPool().execute(() -> doIo(cb)); } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Do not perform IO operations on the UI thread. Use CordovaIn
- Plugin can't handle uri: ${uri}
- Invalid plugin ID in URI: ${uri}
- URI not supported by CordovaResourceApi: ${uri}
- Relative URIs are not supported.
AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22).
Data as JSON: /api/errors/8b8fa95fe8be9bb5.
Report an issue: GitHub.