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

  1. Dispatch all IO from execute() to the pool: cordova.getThreadPool().execute(() -> { ...IO...; callbackContext.success(...); })
  2. Keep only argument parsing/sync checks in execute() itself
  3. 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

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


AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22). Data as JSON: /api/errors/8b8fa95fe8be9bb5. Report an issue: GitHub.