apache/cordova-android · error · IllegalArgumentException

Unsupported keycode: ${keyCode}

Error message

Unsupported keycode: ${keyCode}

What it means

CordovaWebViewImpl.setButtonPlumbedToJs(int keyCode, boolean override) whitelists exactly four Android keycodes: KEYCODE_VOLUME_UP (24), KEYCODE_VOLUME_DOWN (25), KEYCODE_BACK (4), and KEYCODE_MENU (82). Every other keycode falls into the switch's default branch and throws IllegalArgumentException. The built-in callers — CoreAndroid's back-button override and overrideButton("volumeup"/"volumedown"/"menubutton") — only ever pass supported codes, so this is reachable only from plugin or app code calling the CordovaWebView API directly with an unlisted keycode.

Source

Thrown at framework/src/org/apache/cordova/CordovaWebViewImpl.java:420

        appPlugin.fireJavascriptEvent(event);
    }

    @Override
    public void setButtonPlumbedToJs(int keyCode, boolean override) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_BACK:
            case KeyEvent.KEYCODE_MENU:
                // TODO: Why are search and menu buttons handled separately?
                if (override) {
                    boundKeyCodes.add(keyCode);
                } else {
                    boundKeyCodes.remove(keyCode);
                }
                return;
            default:
                throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
        }
    }

    @Override
    public boolean isButtonPlumbedToJs(int keyCode) {
        return boundKeyCodes.contains(keyCode);
    }

    @Override
    public Object postMessage(String id, Object data) {
        return pluginManager.postMessage(id, data);
    }

    // Engine method proxies:
    @Override
    public String getUrl() {
        return engine.getUrl();
    }

View on GitHub (pinned to 7c1e190064)

Solutions

  1. Pass only KeyEvent.KEYCODE_BACK, KEYCODE_MENU, KEYCODE_VOLUME_UP, or KEYCODE_VOLUME_DOWN.
  2. If you need a different hardware key, intercept it yourself by overriding dispatchKeyEvent/onKeyDown in the Activity or a custom View — Cordova's plumbing API will not carry it.
  3. If the keycode arrives from external data, whitelist-check it (see type guard) before calling setButtonPlumbedToJs.

Example fix

// before
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_HOME, true); // throws IllegalArgumentException

// after
if (isPlumbableKeyCode(keyCode)) {
    webView.setButtonPlumbedToJs(keyCode, true);
} else {
    // handle this key in your own dispatchKeyEvent/onKeyDown instead
}
Defensive patterns

Strategy: type-guard

Type guard

private static final Set<Integer> PLUMBABLE_KEY_CODES = new HashSet<>(Arrays.asList(
        KeyEvent.KEYCODE_BACK,        // 4
        KeyEvent.KEYCODE_MENU,        // 82
        KeyEvent.KEYCODE_VOLUME_UP,   // 24
        KeyEvent.KEYCODE_VOLUME_DOWN // 25
));

static boolean isPlumbableKeyCode(int keyCode) {
    return PLUMBABLE_KEY_CODES.contains(keyCode);
}

Try / catch

// Only when the keycode is dynamic and you cannot pre-filter:
try {
    webView.setButtonPlumbedToJs(keyCode, override);
} catch (IllegalArgumentException e) {
    LOG.w(TAG, "Key not plumbable to JS, ignoring: " + keyCode);
}

Prevention

When it happens

Trigger: A plugin calls webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_HOME, true), or passes KEYCODE_SEARCH, KEYCODE_CAMERA, KEYCODE_APP_SWITCH, KEYCODE_DPAD_* (Android TV remotes), KEYCODE_MEDIA_*, or a raw int copied from elsewhere instead of a KeyEvent constant.

Common situations: Android TV / set-top-box plugins trying to plumb remote-control keys through Cordova; code ported from older Cordova forks where extra buttons seemed interceptable; keycodes received from JavaScript or config data passed straight into the native API without validation.

Related errors


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