termux/termux-app · error · JSONException
Both key and macro can't be set for the same key. key: "%s",
Error message
Both key and macro can't be set for the same key. key: "%s", macro: "%s"
What it means
Thrown by the ExtraKeyButton JSON constructor when a single button config object defines BOTH the 'key' field and the 'macro' field. A button must be either a plain key (one key code) or a macro (space-separated sequence), never both. The offending key and macro values are included in the message.
Source
Thrown at termux-shared/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeyButton.java:86
/**
* Initialize a {@link ExtraKeyButton}.
*
* @param config The {@link JSONObject} containing the info to create the {@link ExtraKeyButton}.
* @param popup The {@link ExtraKeyButton} optional {@link #popup} button.
* @param extraKeyDisplayMap The {@link ExtraKeysConstants.ExtraKeyDisplayMap} that defines the
* display text mapping for the keys if a custom value is not defined
* by {@link #KEY_DISPLAY_NAME}.
* @param extraKeyAliasMap The {@link ExtraKeysConstants.ExtraKeyDisplayMap} that defines the
* aliases for the actual key names.
*/
public ExtraKeyButton(@NonNull JSONObject config, @Nullable ExtraKeyButton popup,
@NonNull ExtraKeysConstants.ExtraKeyDisplayMap extraKeyDisplayMap,
@NonNull ExtraKeysConstants.ExtraKeyDisplayMap extraKeyAliasMap) throws JSONException {
String keyFromConfig = getStringFromJson(config, KEY_KEY_NAME);
String macroFromConfig = getStringFromJson(config, KEY_MACRO);
String[] keys;
if (keyFromConfig != null && macroFromConfig != null) {
throw new JSONException("Both key and macro can't be set for the same key. key: \"" + keyFromConfig + "\", macro: \"" + macroFromConfig + "\"");
} else if (keyFromConfig != null) {
keys = new String[]{keyFromConfig};
this.macro = false;
} else if (macroFromConfig != null) {
keys = macroFromConfig.split(" ");
this.macro = true;
} else {
throw new JSONException("All keys have to specify either key or macro");
}
for (int i = 0; i < keys.length; i++) {
keys[i] = replaceAlias(extraKeyAliasMap, keys[i]);
}
this.key = TextUtils.join(" ", keys);
String displayFromConfig = getStringFromJson(config, KEY_DISPLAY_NAME);
if (displayFromConfig != null) {View on GitHub (pinned to 3df69d1da1)
Solutions
- Edit the offending button in the extra-keys config: keep either 'key' or 'macro', remove the other.
- Validate the JSON with a linter/schema that marks key+macro as mutually exclusive.
- If the intent is a macro, use "macro":"ctrl x" and delete the "key" entry.
Example fix
// before (invalid)
{"key": "CTRL", "macro": "CTRL x"}
// after (macro only)
{"macro": "CTRL x"} Defensive patterns
Strategy: validation
Validate before calling
boolean hasKey = config.has(ExtraKeyButton.KEY_KEY_NAME);
boolean hasMacro = config.has(ExtraKeyButton.KEY_MACRO);
if (hasKey && hasMacro) {
throw new JSONException("Button must not define both 'key' and 'macro'");
} Try / catch
try {
new ExtraKeyButton(config, popup, displayMap, aliasMap);
} catch (JSONException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Both key and macro")) {
// remove one field from user config and reload
config.remove(ExtraKeyButton.KEY_MACRO);
} else throw e;
} Prevention
- Treat 'key' and 'macro' as mutually exclusive in any extra-keys config.
- Validate button configs with a JSON schema enforcing exactly-one-of(key, macro).
- When converting a key to a macro, delete the key field.
When it happens
Trigger: An extra-keys JSON config object contains both "key":"..." and "macro":"..." for the same button, e.g. {"key":"CTRL","macro":"ctrl x"}.
Common situations: Hand-editing extra-keys.json and accidentally leaving both fields; migrating a config where a macro was added without removing the key; copy-paste error between button definitions.
Related errors
- All keys have to specify either key or macro
- An key in the extra-key matrix must be a string or an object
- Unsupported TERMUX_APP_PACKAGE_VARIANT "%s"
- Unsupported TERMUX_APP_PACKAGE_MANAGER "%s" with variant "%s
- Unsupported TERMUX_PACKAGE_VARIANT "%s"
AI-assisted analysis of termux/termux-app@3df69d1da1 (2026-08-13).
Data as JSON: /api/errors/b976be782d4f6bf2.
Report an issue: GitHub.