Voice mode fails on Termux/Android: `which` blocked by kernel (EPERM)
Environment
- Platform: Android 13 (Samsung), Termux
- Claude Code version: latest
- Shell: zsh
- Arch: aarch64-linux-android
Description
/voice mode fails on Termux with the message:
Voice mode requires SoX for audio recording. Install SoX manually: ...
SoX is installed and fully functional (sox, rec, play all present at /data/data/com.termux/files/usr/bin/). The issue is not SoX being missing — it's that the availability check itself fails.
Root Cause
The Fn() helper checks for binaries using execFileSync('which', ['rec'], ...). On Android/Termux, spawning which via Node.js execFileSync returns EPERM:
spawnSync which EPERM
This is an Android kernel restriction on certain subprocess spawning patterns — the same restriction that breaks Claude Code's bundled ripgrep on arm64-android. Because Fn("rec") always throws/returns false, the voice availability check always reports SoX as missing regardless of whether it's installed.
Reproduction
On any Termux (Android) install with SoX installed:
# SoX is present:
which rec # → /data/data/com.termux/files/usr/bin/rec
# But Node.js can't run `which`:
node -e "const {execFileSync} = require('child_process'); execFileSync('which', ['rec'], {stdio:'pipe', timeout:3000})"
# → spawnSync which EPERM
Suggested Fix
In the Fn() binary check, fall back to fs.existsSync() across known PATH entries when execFileSync('which', ...) throws EPERM. For example:
function Fn(cmd) {
try {
return execFileSync('which', [cmd], { stdio: 'pipe', timeout: 3000 }).status === 0;
} catch (e) {
if (e.code === 'EPERM' || e.code === 'EACCES') {
// Fallback for Android/Termux: walk PATH manually
const PATH = process.env.PATH || '';
return PATH.split(':').some(dir => {
try { return require('fs').existsSync(require('path').join(dir, cmd)); } catch { return false; }
});
}
return false;
}
}
Related
This is the same class of issue as the bundled ripgrep binary failing on arm64-android (ENOENT for vendor/ripgrep/arm64-android/rg). Both stem from Android's restrictions on native binary execution in certain contexts.
Android users are a real use case — Termux gives Claude Code a fully functional Linux-like environment on a phone, and voice input via SoX would be genuinely useful there.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗