apache/cordova-android · error · IllegalStateException
Do not perform IO operations on the UI thread. Use CordovaIn
Error message
Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead.
What it means
CordovaResourceApi.assertBackgroundThread guards all IO entry points: when thread checking is enabled (the default) and the calling thread is the Android main/UI thread (Looper.getMainLooper().getThread()), it throws IllegalStateException. File and network IO on the UI thread jams the app and can trigger ANRs, so cordova enforces background execution.
Source
Thrown at framework/src/org/apache/cordova/CordovaResourceApi.java:422
outputStream.close();
}
}
}
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;View on GitHub (pinned to 7c1e190064)
Solutions
- Move the IO call onto Cordova's thread pool: cordova.getThreadPool().execute(() -> resourceApi.copyResource(...))
- For UI-initiated work, wrap the whole read/write in the thread pool and hop back with runOnUiThread for UI updates only
- As a last-resort workaround during migration, webView.getResourceApi().setThreadCheckingEnabled(false) — only for debugging, since it removes the ANR protection
Example fix
// before: crashes on UI thread
copyButton.setOnClickListener(v ->
resourceApi.copyResource(srcUri, dstUri));
// after
copyButton.setOnClickListener(v ->
cordova.getThreadPool().execute(() ->
resourceApi.copyResource(srcUri, dstUri))); Defensive patterns
Strategy: validation
Validate before calling
// guard before IO
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalStateException("refuse IO on UI thread");
}
resourceApi.copyResource(src, dst); Type guard
static boolean isSafeIoThread() {
Thread t = Thread.currentThread();
return t != Looper.getMainLooper().getThread() && t != CordovaResourceApi.jsThread;
} Try / catch
if (isSafeIoThread()) { resourceApi.copyResource(src, dst); }
else { cordova.getThreadPool().execute(() -> resourceApi.copyResource(src, dst)); } Prevention
- Route every resourceApi call through cordova.getThreadPool()
- Hop back with runOnUiThread only for UI updates
- Keep setThreadCheckingEnabled(true) in production as a safety net
When it happens
Trigger: Invoking openForRead, openOutputStream, copyResource, createHttpConnection, etc. directly from onCreate/onPostExecute/UI-button handlers or any code running on the main looper — e.g. a plugin doing synchronous file IO inside execute() when called on the UI thread.
Common situations: Plugin execute() assumed to be on a background thread but invoked on UI; refactor moving IO into a click listener; Native code calling the resource API from onResume; occasional crashes only when threadCheckingEnabled is on (default).
Related errors
- Tried to perform an IO operation on the WebCore thread. Use
- 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/d84dad2621acb927.
Report an issue: GitHub.