v2.1.113+ broken on Termux/Android: native binary requires glibc, no JS fallback
Open 💬 65 comments Opened Apr 18, 2026 by tpdhd
💡 Likely answer: A maintainer (ant-kurt, collaborator)
responded on this thread — see the highlighted reply below.
Summary
Starting with v2.1.113, Claude Code switched from a JavaScript entry point (cli.js) to a native glibc Linux binary (bin/claude.exe). This completely breaks Claude Code on Termux (Android), where:
process.platformreportsandroid, notlinux- Android's kernel rejects
ET_EXECglibc ELF binaries (unexpected e_type: 2) - There is no glibc on Android/Termux (it uses Bionic libc)
v2.1.112 (last JS-based version) works perfectly.
Environment
- Device: Pixel 9, Android 14 (non-rooted)
- Termux: latest from F-Droid
- Node.js: v22 (Termux arm64)
- Claude Code: v2.1.113 / v2.1.114 broken, v2.1.112 works
Steps to reproduce
# On Termux
npm install -g @anthropic-ai/claude-code@latest
claude --version
Expected: Version output
Actual:
Error: claude native binary not installed.
Either postinstall did not run (--ignore-scripts, some pnpm configs)
or the platform-native optional dependency was not downloaded
(--omit=optional).
Root cause
install.cjscallsprocess.platform→ returnsandroid→ not inPLATFORMSmap → skips binary install- Even force-installing
@anthropic-ai/claude-code-linux-arm64and copying the binary fails because Android's kernel rejects glibc executables:
````
error: "bin/claude.exe" has unexpected e_type: 2
- Running via
patchelfor the glibcld-linux-aarch64.so.1loader also fails (Could not find a PHDR: broken executable?) - Running via
proot-distroworks (proot-distro login ubuntu -- /path/to/claude --version) but is too slow for interactive use
Workaround
Pin to the last JS-based version:
npm install -g @anthropic-ai/claude-code@2.1.112
Suggested fix
One or more of:
- Add
android-arm64platform support with a statically-linked or Bionic-compatible binary - Keep the JS fallback (
cli.js/cli-wrapper.cjs) functional as a real fallback, not just an error message - Map
android→linuxin platform detection (won't fix the glibc issue alone, but would help if a compatible binary is provided)
Impact
All Termux users on Android are locked out of updates. Termux is one of the few ways to run Claude Code on mobile — a non-trivial use case for developers on the go.
65 Comments
Please fix it, the whole idea of terminal agent is that I do not need IDE in most of the cases now, which means I do not really need my 4 screens , which means no need for a PC. Android termux will th 5 terminals running Claude and doing everything I need is almost all I need these days.
+1, hitting the same regression on Samsung S918B (Galaxy S23 Ultra) running Termux native (not proot).
Environment
5.15.178-android13-8npm install -gReproduction confirmed
process.platformon Termux Node returns"android", not"linux":install.cjsthen falls through theif (platform === 'linux')branch, builds platform key"android-arm64", looks it up inPLATFORMS, getsundefined, and bails with the "Unsupported platform" message — leaving only the 155 KB wrapper stub. Every subsequentclaudeinvocation prints:Real-world impact
This broke an unattended cron job at 09:15 today:
npm update -g --ignore-scriptsran at 08:32, pulled v2.1.114, and the nextclaudeinvocation crashed before any session could start. The script's mid-session resume logic was useless because the session never existed.Workaround verified end-to-end
Two requests for Anthropic
android-arm64entry to thePLATFORMSmap ininstall.cjsandcli-wrapper.cjs(Termux is one of the largest "Linux on phone" environments and this regression cuts it off entirely). Either ship a real Android-bionic native build, or fall back to thelinux-arm64package with a runtime check, or — at minimum — preserve the previous self-containedcli.jsJS entry point as a fallback when no native binary matches.latestor otherwise prevent automatic updates from breaking every Termux user's unattended workflows. Right now anyone withnpm update -gin their cron / Dockerfile / dotfiles bootstrap gets silently broken.Happy to test patches against this device.
Confirming on Termux / Android 13 / aarch64
npm i -g @anthropic-ai/claude-code@2.1.114placed a 236MB native binary at$PREFIX/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exefilereports:ELF 64-bit LSB executable, ARM aarch64, …, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0lddwants/lib/aarch64-linux-gnu/libc.so.6etc. — none exist on Termux (bionic, Android linker at/system/bin/linker64). Binary refuses to exec.package.jsonoptionalDependenciesonly targets glibc/musl linux, darwin, win32 — no android-bionic entry.Workaround:
npm i -g @anthropic-ai/claude-code@2.1.112(last version with"bin": { "claude": "cli.js" }) restores the pure-Node entry and works in Termux immediately.Two requests:
android-arm64/ bionic target ships, could the wrapper fall back tocli-wrapper.cjswhen no \PLATFORMS\key matches, instead of leaving a naked ELF that can't exec? The wrapper file is already shipped in the package but never invoked when the arch genuinely isn't listed.Follow-up: 2.1.114 has the same regression — and the in-process auto-updater is actively reverting manual pins.
Same Termux/Android 13/aarch64 device as my previous comment.
2.1.114 confirmed broken the same way as 2.1.113
npm i -g @anthropic-ai/claude-code@2.1.114→bin/claude.exeis now just the 155-byte error stub (noandroid-arm64inoptionalDependencies, sopostinstallbails). Previouscli.jsis gone.claudeinvocation dies with "Error: claude native binary not installed."New failure mode: the in-process auto-updater silently clobbers a manual downgrade
Inside a running claude session:
npm i -g @anthropic-ai/claude-code@2.1.112— confirmed"version": "2.1.112",cli.jsrestored,package.jsontimestamp fresh.package.json:"version": "2.1.114",cli.jsgone, directory mtime updated.No other process was touching the tree — only the running claude. The built-in updater is on a timer and re-fetches
latest, overwriting the pinned install while the user is mid-session. So for Termux users the current state is:2.1.113+on-disk = broken.2.1.112lasts only until the next updater tick, which can happen within the same session you downgraded from.What actually survives on Termux right now
Belt-and-braces recipe that held:
And in
~/.claude/settings.json:The read-only
chmodis load-bearing — without it, the updater's write silently wins.Concrete asks, restated
DISABLE_AUTOUPDATER=1and skip auto-update when the current install's platform key isn't inPLATFORMS(i.e., don't auto-"upgrade" a user into a broken state on platforms the release doesn't target).cli-wrapper.cjsas an automatic fallback when noPLATFORMSentry matches the host. Shipping the file and never using it is a footgun.2.1.112asnpm dist-tag add @anthropic-ai/claude-code@2.1.112 termux(or similar) so the downgrade command is documentable and doesn't rot.Happy to test any patch on the same device.
For anyone landing here looking for a ready recovery path, I maintain claude-code-android specifically for this install target and shipped v2.7.0 last night with the pinned install plus the auto-updater defense.
@daniel-thisnow nailed the auto-updater issue above. Credit to him for the chmod finding. The v2.7.0 install script bundles his defense into an idempotent fresh-install / recover-in-place flow so users can go from the broken error to a working claude in one command.
Two install paths in the repo:
Repo: https://github.com/ferrumclaudepilgrim/claude-code-android
I will keep it current as the upstream situation evolves.
Confirming on Samsung A55, Termux native (no proot), aarch64
Same
claude native binary not installedonnpm i -g @anthropic-ai/claude-code@latest. My setup:process.platformreturnsandroid, archarm64/data/data/com.termux/files/usr/lib/node_modulescliwrapper.cjsis in the package but there's no fallback. Running it directly just surfaces the same "unsupported platform" error frominstall.cjs. The file ships but never runs, which is the footgun the OP called out.What's holding for me
Pinned to 2.1.112 (last version with a working
cli.js) with three layers of lock against the autoupdater silently winning:In
~/.bashrc:In
~/.claude/settings.json:Without the read-only lock
chmod -R a-w, the updater's write can still win against the env var in some paths and silently upgrade you back into the broken state.Echoing the asks
DISABLE_AUTOUPDATER=1everywhere, and skip auto-update when the current platform key isn't inPLATFORMS. Auto-upgrading users into a broken state on unsupported platforms is worse than not updating them.cliwrapper.cjsas a real fallback whenPLATFORMShas no match. The file is already in the package. Making it a trap instead of a safety net is reversible.@anthropic-ai/claude-code@termuxpointing at 2.1.112 would make the workaround documentable and stop the downgrade command from rotting.Why this matters
Termux is how a lot of people run dev on Android. For me it's the phone side of an SSH-into my Debian workflow, with local Claude Code as the fallback when I'm offline or the server's not reachable or when i need to tweak Termux with Claude. Losing that breaks the "check on the agent loop from anywhere" use case, which is exactly what Claude Code agentic workflows are designed for.
Happy to test fixes on the android-arm64 path.
Deep-dive + proposed minimal patches
Inspected the actual 2.1.114 npm tarball. The failure path is small and easy to fix even without shipping an Android binary — three compounding bugs each have a ~5-line fix:
Bug 1 —
install.cjssilently no-ops on Android, leaves a misleading stubinstall.cjsline 127-137 in 2.1.114:On Termux,
process.platform === 'android', sogetPlatformKey()returns'android-arm64',PLATFORMS['android-arm64']isundefined, andmain()returns with exit code 0. npm reports a successful install. The stubbin/claude.exe(a 500-byte shell script) stays in place, and the user only discovers breakage at firstclaudeinvocation — with an error message that points them in the wrong direction (see Bug 2).Bug 2 — the stub error message gives misleading advice on Termux
bin/claude.exestub content:On Termux, none of this advice applies — postinstall ran, optional deps downloaded, the binary was just skipped because the platform isn't in
PLATFORMS. Users follow the advice, reinstall, hit the same error, and conclude it's just broken.Bug 3 — auto-updater re-breaks the pinned install
Even after
npm install -g @anthropic-ai/claude-code@2.1.112 --force, something in the claude CLI auto-upgrades to@lateston next launch, silently re-bricking the install. Users hit the native-binary error a second time and don't understand why. The only reliable fix on Termux right now is also settingDISABLE_AUTOUPDATER=1in settings/env — which isn't documented for this failure mode.Proposed minimal patch (no Android binary required)
Just detecting Android in the two shared
PLATFORMS/getPlatformKey()code blocks and failing loudly with correct advice would save every Termux user from this confusing debug loop. Proposed diff againstinstall.cjs:Same block should be added to
cli-wrapper.cjsinsidegetBinaryPath()for the--ignore-scriptspath.Broader fix (auto-updater)
The auto-updater inside the CLI should skip itself on
process.platform === 'android'(or at minimum pin to@2.1.112instead of@latest) until an Android binary is shipped. Right now it actively breaks working installs every few hours — arguably worse than the install bug itself.Ideal fix
Ship
@anthropic-ai/claude-code-android-arm64built against Bionic (or statically linked). Termux has ~1M+ installs; this is a non-trivial userbase. Happy to test an alpha build on Pixel 9 / Android 14 if it'd help.---
Environment: Termux on Android 14 (aarch64), Node 22, npm 10.
how is this issue still open bruh
Seems like we are being forced to use the app???
@aryehbeitz is spot-on. It is bewildering for those of us who have been using Claude Code on Termux to suddenly find it bricked with the misleading stub error message after the updater silently broke it.
I am happy to report Codex (@mmmbuto/codex-cli-termux) continues to work on Termux.
Anthropic, get this fixed. Termux is popular and heavily used by those who need it. Even the old version on Google Play shows it has 10M+ downloads.
Confirming on 2.1.116 / Termux / Android (aarch64-linux-android), kernel
5.15.178-android13-8. Same regression —npm updatebumped from 2.1.112 to 2.1.116,install.cjsrejected withUnsupported platform: android arm64,bin/claude.exeis the 500-byte stub, everyclaudeinvocation dies.Additional data point on the musl fallback: I tried
npm install -g @anthropic-ai/claude-code-linux-arm64-musl@2.1.116 --forceto sidestepinstall.cjsentirely. Install succeeds, but the binary itself refuses to exec:The musl variant isn't a viable escape hatch on Termux either — it's dynamically linked against musl's loader, which Android's Bionic doesn't provide (unlike truly-static musl binaries like ruff/uv, which do work fine on Termux). Either a genuine \
android-arm64\build or a statically-linked musl binary would unbreak this.Rollback to 2.1.112 works as described above. +1 on restoring a \
cli.js\/ \cli-wrapper.cjs\JS fallback when no \PLATFORMS\key matches.While waiting for a fix:
Then update
~/.claude/settings.jsonwith:Claude Code 2.1.113 has switched to a native executable (after npm deprecation was announced in January), which drops support for some platforms. We may add support for Android in the future - this requires either a bun target for
android-arm64or static musl.You can pin to the last JS bundle release https://www.npmjs.com/package/@anthropic-ai/claude-code/v/2.1.112 to continue using Claude Code for now.
+1 from a Haiku OS user. Same root cause as the Termux reports:
process.platformreturnshaiku, which isn't ininstall.cjs's PLATFORMS map, so the postinstall skips the native binary download entirely and the npm wrapper fails at launch with "claude native binary not installed."I've been running Claude Code happily on Haiku for months — it worked because the pre-2.1.113 CLI was plain Node.js and didn't care what platform it ran on. The switch to a platform-native binary wasn't aimed at platforms like mine, but it hard-fails on them rather than degrading gracefully.
I'm currently pinned to 2.1.112, which means I'm missing the fixes from the April 23 post-mortem (notably the verbosity system prompt reverted in 2.1.116). Staying pinned isn't sustainable long-term.
The ask here isn't "officially support Haiku" — I understand that's a real engineering commitment. The ask is much smaller: restore a JS fallback for platforms where no prebuilt binary exists. When
process.platformisn't in the PLATFORMS map, fall back to the bundledcli.jspath that worked fine through 2.1.112, possibly behind a flag or with a warning that the platform is unsupported. This would unblock Haiku, Termux/Android, FreeBSD, and any future platform without requiring Anthropic to maintain binaries for them.Happy to test patches on Haiku if that would help.
Workaround refinement not yet in this thread:
npm install -g @anthropic-ai/claude-code@stableworks identically to@2.1.112today, and is more durable than version-pinning. As of 2026-04-25, thestabledist-tag still points to2.1.112:Anthropic kept the last pure-JS release as
@stablerather than promoting any native-binary version. Pinning to@stableis preferable to@2.1.112because:2.1.112ever gets unpublished,@stablekeeps working2.1.112.xpatch backport ships,@stablepicks it up automaticallyCombine with
"autoUpdates": falsein~/.claude/settings.json(per @sb-dev) to defend against the in-session auto-updater silently re-bricking the install, and this is a one-shot, persistent fix:Confirmed working 2026-04-25 on Samsung S26 Ultra (Android 16, kernel 6.12.30-android16, Termux 0.118.3, Node 24.14.1, npm 11.13.0) — adding to the device-confirmation list since Android 16 / S26 hardware hasn't been reported yet in this thread.
Same issue on OpenBSD x64. The native binary is a glibc Linux ELF that can't run on OpenBSD (no Linux emulation layer in OpenBSD 7.x). I'm currently pinning to 2.1.109
The fix request is the same: restore a JS fallback path for platforms not supporting a native binary. I'm fine to eat the performance cost for more cross-platform support. I understand you can't ship a binary for everything.
DeepseekPro4 is another beast. I've switched.
On Fri, May 1, 2026 at 7:11 PM cowile @.***> wrote:
--
Oscar Espinoza
oscarespinoza.co.uk <http://oscarespinoza.co.uk>
+1
I hope this gets support.
Run the current native binary on Termux (alternative to pinning to 2.1.112)
Patchelf the linux-arm64 binary's ELF interpreter to glibc-runner's ld.so, exec it directly with
LD_PRELOADcleared so termux-exec doesn't crash it on startup, then re-applyLD_PRELOADvia claude's settingsenvso subprocess shebang resolution still works.Updated 2026-06-01 to keep
LD_PRELOAD(so#!/usr/bin/envshebangs in pnpm/npx and your own scripts keep resolving) and fix the bundled grep/find/rg that the re-injectedLD_PRELOADotherwise breaks — via a length-preserving binary patch plus a compiled launcher wrapper. This avoids the lossy alternative of droppingLD_PRELOADentirely, which silently breaks every#!/usr/bin/env-shebang tool.One-time setup:
Install / re-run on each version bump (set
VERSIONto the desired release):Add to
~/.claude/settings.json:autoUpdates: falsestops the in-session updater from re-downloading the broken bundle. TheLD_PRELOADentry is applied after the dynamic linker finishes with claude (so it doesn't crash the main process), but is inprocess.envwhen claude spawns subprocesses, so#!/usr/bin/env fooshebangs in tools like pnpm and npx resolve correctly inside the Bash tool.Chezmoi-managed version with Renovate-pinned releases: gtbuchanan/dotfiles.
Can confirm the gtbuchanan method works!
One small note for anyone like me that uses fish instead of bash: replace the .bashrc step with
fish_add_path $HOME/.local/binDifferent methods for different shells...
Thank you, @gtbuchanan!
Nice! Thanks for this!!
Here's a script that automates it a bit better for you. You can just run this every time you wanna check for updates and update. It'll handle all the first time setup and conflicts for you.
You can save this to a file and run it whenever you wanna try updating
Great script, one problem though, Claude now complains that grep does not properly work and if I ask it fix it it starts talking about it's internal tools and I am afraid to break it.
The shell grep/rg tooling is corrupted in this environment. Let me use the Read tool directly, which is reliable
npm was fine.
Ran the script on Termux (Android, aarch64, glibc-runner already set up). Had to deviate at several points to get a working install + avoid the grep break @cyberb is describing — and to recover old sessions from the npm version.
The grep breakage: it's the
settings.jsonblock that setsenv.LD_PRELOAD = libtermux-exec-ld-preload.so. The wrapper correctlyunset LD_PRELOADfor the main claude process, but the settings env reinjects the bionic preload for everything claude spawns — including its ownexec -a ugrep $CLAUDE_CODE_EXECPATHre-exec for the bundled grep. Bionic preload hasDT_NEEDED libc.so(unversioned); the glibc child's ld.so resolves that to$PREFIX/glibc/lib/libc.sowhich is a text linker script, not an ELF → "invalid ELF header" → Grep/Find tools die. Removing theenv.LD_PRELOADfrom settings entirely fixes it; the wrapper'sunsetis sufficient. SessionStart hooks still work fine because/bin/shdoesn't need the preload for normal operation.Old npm sessions disappear from
/resume— andcd && claude --resume <id>errors "different directory". Root cause:/homeis a bind mount of/data/data/com.termux/files/home(same inode), not a symlink. The old npm claude (bionic + libtermux-exec preload) hadgetcwdintercepted to return the long canonical path. The new glibc claude has no such preload, sogetcwdreturns the kernel-tracked/home. Slugs and recordedcwdfields don't line up. Migrating the old jsonls fixes it:cwdfield from/data/data/com.termux/files/home(/...)to literally/home(flat — not/home/subdir, otherwise resume cwd-check fails when launching from$HOME).~/.claude/projects/-home/.sed, notjq. jq re-serializes the whole line; even key-order/unicode-escape drift breaks the cryptographic signatures on extended-thinking blocks and the API rejects on resume with400 "thinking or redacted_thinking blocks in the latest assistant message cannot be modified". A targetedsed -E 's|"cwd":"/data/data/com\.termux/files/home(/[^"]*)?"|"cwd":"/home"|g'keeps everything else byte-identical.touch -r <src> <dst>after each copy so file mtimes stay accurate (otherwise/resume's sort-by-recency shows every old session as 1 minute old).Other patches needed:
curl "$BASE_URL/latest/manifest.json"404s. Anthropic's actual endpoint is$BASE_URL/latest(plaintext version), then$BASE_URL/$VERSION/manifest.json. The officialinstall.shdoes it this way.patchelfnot on PATH (line 129).patchelf-glibcinstalls the binary at$PREFIX/glibc/bin/patchelf, not anywhere defaultPATHwill find. The barepatchelfcall fails "command not found". Use the full path.$PREFIX/etc/termux/chosen_mirrorsset,pkg install glibc-reposilently sits at "No mirror or mirror group selected" forever waiting for stdin. Pin before running:ln -sf $PREFIX/etc/termux/mirrors/default $PREFIX/etc/termux/chosen_mirrors.pkg install glibc-*step's memory spike can take the whole process tree out via signal 9. Detach withsetsid nohup bash ~/update-claude.sh </dev/null >log 2>&1 & disownor just run it in a plain Termux shell outside claude.(Once these are all in, 2.1.156 works end-to-end with Grep/Find/SessionStart functional and old npm sessions visible.)
@kingpanther13 first of all thanks for the insight and testing you've made. I'm in middle of fixing the things you mentioned.
Some of the stuff you mentioned I never had in any of my phone's termux. You seem to be using an old version of the app or possibly from the play store...
Regarding the main issue: grep and find not working, your solution is definitely the best so far, but I hate the idea of not having all my scripts work in Claude (shebangs), and I also don't know what else termux LD_PRELOAD provides.
The ideal would be (actually the ideal would be to have Claude support termux or switch to codex ;) but practically)
To have Claude started without LD_PRELOAD and instead of having it loaded into all it's child processes, we should load it only when it calls the run command tool (which I believe or hope it's internal grep and find skip). Setting it as an env variable in it's hooks didn't help.
Honestly, I don't enjoy wasting time with some random stupid hook API to get something this simple working. I prefer actually doing useful interesting coding and engineering so I'm kinda pissed at Claude and out of patience for the time being...
And btw shame on Claude. Huge projects (just off my head: python, xh, fd and so much more) officially support Android and termux. A shame Claude won't...
K it seems you're right. Only shebangs affected by removing it. So that's what we'll do. We'll just have to remind Claude to use bash path instead of ./path. Here's the updated script.
Here are the changes:
The /home is never mounted in normal termux so not fixing.
Yeah I'm on latest Termux 0.118.3, and mine is from F-Droid store but apparently the Google play version is the same now. I mostly use this for coding on my ha-mcp and Hubitat repos remotely, and it has been touchy because Claude forgot how to run unit tests and ruff and stuff like that with this new setup but it finally got it squared away.
Follow-up correction. The
/homecwd / session-migration section in my prior comment is NOT generally applicable — it was caused by a pre-existingalias claude='termux-chroot claude'on my.bashrcline 15, which predated the install by 9+ days (oldest retained shell snapshot showing the alias is 2026-05-19; install was 2026-05-28).termux-chrootis Termux'sproot-based wrapper that re-roots the filesystem under/data/data/com.termux/files, making the home dir appear at/home. That's the entire source of the cwd mismatch — not the new binary, not anything in your script.If you're hitting "this conversation is from a different directory" / 227-old-sessions-disappeared on
/resume, runtype -a claudefirst. If it's aliased to anything proot-based, the migration section applies. If not, you can skip it.Also: on my setup the new binary turns out to be load-bearing on the chroot — running
~/.local/share/claude-code/claude --versionfrom a clean shell withunalias claudeerrors witherror while loading shared libraries: /data/data/com.termux/files/usr/glibc/lib/libc.so: invalid ELF header(same text-linker-script trap from gotcha #5 / patchelf comment block). Insidetermux-chroot's proot, path translation routes the runtime lookups to the real versionedlibc.so.6and the binary loads fine. Other Termux setups (yours included, I'd guess) don't appear to need the chroot, so this is something specific to how myglibc-runnerinstall sits, not anything in the script. Not asking you to fix it — just flagging it for anyone with a similar setup who runs into the load-bearing chroot situation.The other four deviations from my prior comment (
/latest/manifest.jsonURL,patchelfnot on PATH,chosen_mirrorsstall, Android lmkd OOM during heavy install) are still general and worth keeping in the script.Yup that's why I fixed only the actual issues. (Patchelf, version and grep)
Chosen mirrors isn't normal. Firstly even first time running pkg it usually selects mirrors itself. Secondly, I highly doubt anyone is installing Claude as their first pkg install. Common. Your workaround has side affects and isn't necessary. You can change the script locally for you if you want to...
The oom issue shouldn't happen on phones that aren't very old. It's not such a heavy install. If it does then that's on you and often requires root depending on the phone and os. Not something to implement in the script.
I tried to let it select mirrors on its own but it didn't work. To be clear Claude managed most of this for me, but I'm pretty sure Claude screwed up my path several months ago and this script is what revealed it. Claude absolutely was my first pkg install, it is pretty much all I use Termux for.
As for the oom thing I'm running a Fold5, I wouldn't exactly call it old, it has 12gb of RAM. Running the script in its original state crashed all of my other Termux sessions, and would crash its own session even when it was the only one.
Dunno what to tell you. I ran it on a phone that has literally 2 GB of RAM!!! No crash at all.
I have many phones and installed termux many times. Never did I have to select mirrors manually. I genuinely don't know what's wrong by you. Extremely weird
Removing
LD_PRELOADfrom settings drops support for Node/Python shebang scripts (e.g.,pnpm), which is arguably worse than losing Claude's bundled grep/find/rg functions (I've been working just fine without them). Nonetheless, I've updated my standalone script to workaround the grep problem (requiresclangpackage) and fixed the minorpatchelfpath issue. As stated, I don't use this exact script because I use Chezmoi/Renovate. Just trying to help y'all out.Amazing and brilliant!! Thanks!!
Only trade-off would be, of anthropic modified their code in a way that breaks this regex. (I know you hardcoded a version in your script, but I mean if someone would edit that - to get newer versions then that)
Unless this is something they probably won't modify for the foreseeable future? (I don't know Claude code structure...)
But thanks a lot!
Yes, that's definitely a risk. tweakcc has this problem often. I avoided hard-coded minification tokens, so the risk is minimized somewhat, but it's still a possibility. Hopefully, this is just a short-term workaround...
@gtbuchanan do you mind if I make a repo with 2 scripts
Both will do the whole before and after process to make everything work.
Will obviously credit you
Hope it's okay with you
https://github.com/flipphoneguy/claude-code-termux
Documented with both scripts there
@flipphoneguy I'm fine with you publishing the scripts yourself as long as you credit me. However, I decided to go ahead and release a polished
debpackage with all of the Claude Code Termux workarounds I've been using (includingTMPDIR; see #15637).I am using a GitHub Action to monitor Claude Code releases and run end-to-end (E2E) tests against them to detect when patching fails. I also decided to dogfood my own package to keep all of the logic out of my dotfiles repo, and so I'll be more likely to keep up with it 🙂
@cyberb I created the https://github.com/claude-yolo/claude-yolo and it should support installation in Termux
Here is one-liner (macOS, Linux, WSL, Termux):
If you want to connect to your desktop from your Android phone via Tailscale or Wireguard, you can try https://mobile-ssh.github.io.
Wow! This is amazing!! Thanks a ton!! I'll check that out soon. Really love your work!
@dkozlov Did you actually read the issue? From what I can tell, your tool downloads the Claude Code npm package, which doesn't work on Android anymore. Also, take a look at continuous -claude 🙃
Follow-up from my April 18 comment (here) — same Samsung S918B, same Termux native arm64 (no proot), now on kernel
5.15.189-android13-8. After two months of staying on the 2.1.112 pin, I tried gtbuchanan/claude-code-termux v2026.6.18 today (which uses Termux's glibc-runner + patchelf-glibc) and hit a new failure mode that's worth surfacing here for anyone tracking Termux:The community workaround that worked on older Android kernels no longer works on Android 16 / kernel 5.15+. strace shows the kernel routes every glibc ELF through
/system/bin/linker64regardless of the binary's patchedPT_INTERP:Bionic linker then can't load libstdc++.so.6 from glibc's path; even with the RPATH coerced, it bombs on
STB_GNU_UNIQUE(binding value 10) when resolving libstdc++ symbols.Same symptom via
grun(Could not find a PHDR: broken executable?) and via directly invoking the glibcld.so(same PHDR error — kernel rejects it too).So the patchelf-and-exec approach the community settled on is not a permanent fix; newer Android kernels regressed it from userspace's perspective. Filed the deep dive on the launcher repo: gtbuchanan/claude-code-termux#20.
I'm back on
npm install -g @anthropic-ai/claude-code@stable(= 2.1.112) until either (a) Anthropic shipsandroid-arm64inoptionalDependencies(this issue), or (b) the termux-pacman community adds Box64-style binary translation, or (c) someone in the thread finds a userspace trick that survives the new binfmt_elf behavior.Bumping this with concrete evidence in case it nudges priority — Termux on modern Android is now hard-stuck on 2.1.112, no glibc-runner workaround available.
I'm still able to run 2.1.178 on my proot version, and 2.1.158 on my bare Termux version (Claude made my setup weird, so I have two.) I tried updating my proot version to 2.1.185 and it hit a BUN error, so that is failing now too. So you don't have to be stuck on .112 at least. The way I've been handling this is I have Claude run the update for me in one session, and I open a new session to see if it works...the old session remains untouched since it is loaded into memory. If new session doesn't work then I have the old session revert it.
I'm going to try and mess around with it and see if I can get it to update in proot environment to latest (.185) and I'll share anything I learn. Claude works pretty much the same in either environment for me, was a bit annoying to set up initially but all I have to do is just type Claude in my main command line and it launches just as it should, so running it in proot really doesn't affect anything other than making it a bit weird for it to run some certain programs.
@kingpanther13 thanks for the data point — the "2.1.158 on bare Termux" case is the interesting one. Could you share your
uname -aand your Android version?On my Samsung S918B / Android 16 / kernel
5.15.189-android13-8, every glibc-runner / patchelf-glibc approach fails because the Android kernel substitutes/system/bin/linker64regardless of the patchedPT_INTERP— strace evidence + four failure modes in gtbuchanan/claude-code-termux#20. If you're on an older Android/kernel that still honorsPT_INTERP, that would explain why bare-Termux 2.1.158 still works for you, and pin down a kernel-version window for everyone else hitting the same wall.(Also: the proot-2.1.185 BUN error you saw is probably bun#32489 — closed upstream 2026-06-18 with an
epoll_pwait2ENOSYS fallback; whatever Bun version Anthropic bundles in 2.1.185 likely predates that fix.)@salviz — full data below. Short version: we're both on Android 16, so it's not an old-Android thing on my end. PT_INTERP is honored on my device but not yours, which points at a device/ROM difference rather than a kernel-line regression. And yes, the proot 2.1.185 crash is bun#32489 — with a proot workaround that gets to latest today.
My environment
Same Android 16 and same
android13-8GKI branch as you — I'm actually one patch behind (.178vs your.189).Your PT_INTERP theory, tested directly — same strace you ran, bare Termux (TracerPid 0, no proot):
My kernel honors the patched
PT_INTERP: a single execve straight to the glibc binary, no substitution to/system/bin/linker64. The patchelf-glibc Claude build (2.1.158) runs natively in bare Termux with no proot — the opposite of your S918B result. Since we're both on Android 16 / 5.15.x-android13-8, it isn't the Android version or the GKI line; it looks device/ROM/firmware specific (F946U vs S918B, or a change in the .178 -> .189 patch window). The linker64 substitution is not a universal Android-16 wall — it didn't reproduce here.Two distinct failure modes (the thread keeps merging them):
/system/bin/linker64. Device-specific (works on my F946U, fails on your S918B). proot sidesteps it entirely — it gives the glibc binary a normal Linux rootfs view, so it loads regardless of the bionic-linker behavior.epoll_pwait2has no ENOSYS fallback, segfault at 0x0. First crashing release is 2.1.181 (Bun 1.4.0; 2.1.179 is still fine, 2.1.180 was never published) on any reported kernel >= 5.11. Affects both bare and proot. Fixed upstream 2026-06-18, not yet in a shipped Claude release.The proot 2.1.185 crash is mode 2 — and here's exactly how I run latest under proot. I decoded the bun.report stack:
sys_epoll_pwait2 (linux.rs)->us_loop_run_bun_tick->process_events->HTTPThread.on_start, null deref at 0x0 — exactly bun#32489. Bun only takes theepoll_pwait2path when the reported kernel is >= 5.11, so I make proot report an older kernel and Bun falls back toepoll_pwait.termux-chrootdoesn't pass-kthrough, so I copied its bind list into a launcher and added one flag —-k 5.10.0:(I trimmed a couple of optional/conditional binds for brevity — grab the full list from
$PREFIX/bin/termux-chrootif you want it verbatim. Theclaudeinside is the normal patchelf-glibc native binary — ELF interpreter repointed to glibc's ld.so, same install everyone uses; proot just gives it a Linux rootfs view so it loads, and-k 5.10.0keeps Bun offepoll_pwait2.)Confirming it's exactly the kernel gate:
-k 5.11.0-> still segfaults-k 5.10.0-> 2.1.185 launches and runs a normal interactive session (confirmed)PROOT_NO_SECCOMP=1alone -> still crashes (so it's syscall selection, not seccomp)So under proot nobody's stuck on .112 — latest runs with
-k 5.10.0; only side effect isunamereads 5.10 inside that proot.Bare Termux ceiling (bisected on my device): bare Termux runs up to 2.1.179; 2.1.181 is the first release that crashes with bun#32489 (2.1.180 was never published). So bare isn't stuck on .112 either — it's good through 2.1.179 on a PT_INTERP-honoring device. There's no
-kescape hatch in bare Termux, so anything newer than .179 needs a Claude release that bundles the fixed Bun.+1 to hopefully have Anthropic fix this for Android Termux users. I'm also stuck using 2.112 on Termux bare. Tried all of the patched repos available and none work with the latest CC native install. Also can't seem to get proot-distro Ubuntu working for some reason.
Fwiw I literally just ask my Claude to set it up for me lol, it made the proot thing without my prompting long before this was a problem, I'm not entirely sure why it did it but that's been my saving grace thru all of these changes. So you might benefit from just asking yours to set it up, it may be able to figure it out for you... Just make sure to never close your active session window, always open new sessions to test that way if it screws up you go back to the original active session and have it fix it for you.
So right now you can get up to 2.1.158 (at least I can using this in native Termux.)
In proot-distro inside of Termux I can successfully run 2.1.185 with no problems.
The bun fix #32490 landed and should fix one of the newly created issues from what I can see. I haven't had a chance to test it as big boy world has kept me super busy but the fix is still 100% Anthropic stepping the game up for us.
My S26 Ultra is running 2.1.176 in Termux but since its my daily driver/ use its heavily modded/unreliable as a test case. I have 4 other Android phones I am hoping to get a full day at the desk to poke around a bit.
@kingpanther13 perfect data, thank you — that nails it down. We're both on Android 16 /
5.15.x-android13-8GKI, but your F946U honorsPT_INTERPand my S918B re-routes through/system/bin/linker64. So it's device/ROM/firmware, not a kernel-line regression. For the comparison record, my S918B build:Two open questions where you'd help me (and the next Samsung S-series user) narrow it further:
ro.build.fingerprint(the full string) — if it's asamsung/...variant, we can see whether it's an S-series vs Fold-series Samsung kernel-config delta or something deeper.getenforce(orcat /sys/fs/selinux/enforce). I can't read mine here (it returns "unknown" via getenforce as a Termux app). If your device is Permissive that would explain a lot; if Enforcing on yours and Enforcing on mine, it's something else.And huge thanks for the
proot -k 5.10.0walkthrough + the bisect (2.1.179last bare-good,2.1.181first Bun-crash). I'll keep this thread updated with anything I find on the S918B linker64 routing — looks like I should also file a Samsung-specific case in termux/termux-packages if no one else has.@salviz —
Fingerprint:
SELinux: Enforcing. I can't read
/sys/fs/selinux/enforcedirectly either — it returnsPermission denied. But that denial is the answer: the node is world-readable by DAC, and in Permissive mode the read just succeeds and prints0. Getting EACCES means SELinux actually enforced the policy → Enforcing.So we're both Enforcing — that rules out the permissive theory. Two things still differ:
q5q(Fold5) vs yourdm3q(S23) — different Samsung device trees on the sameandroid13-8GKI base.BP2A.250605…/ security patch2025-10-01; yours isBP4A.251205…/2026-05-05— ~7 months newer. So thelinker64re-routing could be a newer-firmware regression as much as an S-vs-Fold delta.If you open the
termux/termux-packagesSamsung case, add my F946U as the contrast point: honorsPT_INTERP, SELinux Enforcing, fingerprint above, older firmware.Workaround for this bug is https://mobile-ssh.github.io/compare/ and https://github.com/claude-yolo/claude-yolo#installation single line installer
@kingpanther13 great — that locks the diagnosis. Both Enforcing rules out SELinux mode; the EACCES-on-read trick is a nice touch.
So the difference is now two-dimensional and both are real candidates:
q5q(Fold5) honorsPT_INTERP;dm3q(S23) routes through/system/bin/linker64. Different Samsung product trees on the sameandroid13-8GKI lineage.BP2A.250605…(Oct 1, 2025 patch) vs myBP4A.251205…(May 5, 2026 patch) — ~7 months newer Samsung build, kernel5.15.178vs5.15.189(11 patches).Either could be load-bearing. The cleanest split would be a Z Fold5 user on a 2026 build (would isolate to firmware age) or an S23 user on a 2025 build (would isolate to device tree). I'll see if I can dig up either in the thread.
Will file the
termux/termux-packagesissue with both fingerprints as the contrast pair. Thanks again — this is the most productive thread I've had on the topic.—
@dkozlov for context:
claude-yolodoesnpm install -g @anthropic-ai/claude-codeunder the hood, which is broken on Android post-2.1.112 (#50270 is exactly that root cause — same failure mode the OP reported). It's a sandbox-bypass tmux wrapper, not a native Termux runner.mobile-ssh.github.io/compareis a desktop-runs-Claude / phone-SSHes-in setup, which works but requires keeping a desktop online and routes around the "Claude Code on Termux natively" problem rather than solving it.I managed to work around the uname issue in claude-code-termux without resorting to proot. I'm running Claude Code 2.1.185 natively on Termux now.
@gtbuchanan thanks for shipping that —
v2026.6.19lands the uname shim without proot. Two things from my side:proot -k 5.10.0for any PT_INTERP-honoring device.gtbuchanan/claude-code-termux#20: the kernel substitutes/system/bin/linker64for the patchedPT_INTERPon my device, so the glibc ELF never reaches bun at all. Per @kingpanther13's comparison fingerprint vs mine (#issuecomment-4763033560), candidates are (a) Samsungdm3q(S23 Ultra) vsq5q(Fold5) device-tree delta, or (b) the firmware patch window — hisBP2A.250605…(Oct 2025 patch) works, myBP4A.251205…(May 2026 patch) doesn't. Both Enforcing SELinux.I'll test
v2026.6.19on a throwaway prefix anyway in case the install path has shifted enough to bypass my linker symptom — but if it still hits the linker64 substitution, that issue is separate from the bun gate this PR fixes. Will update#20with the result either way.@gtbuchanan confirmed working here too —
install.shgets 2.1.185 running natively (no proot) on my SM-F946U (Fold5, Android 16, kernel5.15.178-android13-8). Noticeably smoother than theproot -k 5.10.0route I'd been on: no ptrace tax, instant keyboard, no startup lag. On a PT_INTERP-honoring device the uname-shim is clearly the better fix.A few odds and ends from switching over from a proot setup, in case they help anyone:
~/.local/bin/claudeuntouched (it only reconciles a symlink there), so my old proot launcher kept working side by side.settings.jsonenv.LD_PRELOADmerge. It's correct/required for bare (re-arms termux-exec for subprocesses). But if you also run a proot glibc claude sharing the same~/.claude/settings.json, that key breaks the proot one's grep/find: the glibc binary re-execs itself asugrep, inherits the bioniclibtermux-execpreload, and glibc's ld.so then resolveslibc.soto a text linker script →invalid ELF header. If you keep both, install withCLAUDE_CODE_SKIP_SETTINGS=1or give the bare instance its ownHOME. The bare claude itself is unaffected — itsugrepre-exec routes through the glibc uname-shim, so grep/rg/find all work./tmpcaveat is the only limitation and doesn't touch normal MCP servers.~/.claude(settings, plugins, MCP, creds) carries over if you keep the sameHOME, but auto-memory and/resumehistory are keyed by the cwd-encoded project slug — proot's/homeand bare's real/data/data/.../homehash to differentprojects/<slug>/dirs, so those don't follow automatically. Copying sessions means rewriting the"cwd"field in each.jsonland re-applying the original file mtimes (or/resumeshows everything as "just now").Thanks for shipping this — clean answer for PT_INTERP-honoring devices.
@kingpanther13 glad it works on the Fold5 — and the migration notes (proot-sharing-
~/.claudegotcha, the cwd-slug rewrite tip, MCP coexistence) are gold for anyone landing here from a proot setup. Pinning those mentally.One quick verification — on my Termux (standard F-Droid build,
https://termux.netstable/main),termux-execcandidate is1.9and onlylibtermux-exec.soships (no-ld-preload). For PR #22 to be relevant on your install you'd need either (a) the new postinst that detects the available lib name (only onmain, not yet inv2026.6.19), or (b)libtermux-exec-ld-preload.soalready present. Did you runinstall.shfrommainrather than the released.deb, or do you happen to have the-ld-preloadvariant from a different repo (termux-pacman, x11 nightly, etc.)? Asking because if 1.9 with justlibtermux-exec.soworks for you underv2026.6.19somehow, that's a useful data point — and if you grabbedmaindirectly, even better confirmation that the upcomingv2026.6.20is the cleaner ride for everyone else.Either way — thanks for stress-testing the bare-Termux path. Saved me a lot of guessing.
@salviz it's the termux-exec version, not
main— I ran the releasedv2026.6.19.deb.What made it work: my termux-exec is the current
1:2.4.0-1(officialpackages-cf.termux.dev/apt/termux-main). In 2.x the lib was split, and it shipslibtermux-exec-ld-preload.sodirectly, with the old monolithic name kept as a compat symlink:So
v2026.6.19's hardcoded path resolves with no help — not because I'm onmain, but because current termux-exec already provides-ld-preload. Yourtermux.net/1.9is the older set (the F-Droid Termux build tends to be stuck there), where only the monolithiclibtermux-exec.soships — exactly the case PR #22's lib-name detection covers. tl;dr: on a current termux-exec the released.debis fine as-is; the detection PR is what unblocks the older 1.9.Separately, since I'd cited it: a maintenance OTA bumped me to the
2025-11-01patch since my last post, but the kernel's unchanged (5.15.178-android13-8) and PT_INTERP still holds — so on this device it tracks the kernel line, not the monthly patch.@kingpanther13 perfect — that's the missing piece. Confirmed on my side:
Old
termux.netmirror, 1.9 frozen there. Probingpackages-cf.termux.dev/apt/termux-maindirectly shows the 2.x split (termux-exec+termux-exec-static), matching what you have. So my next step is switch the mirror to the modern endpoint, upgradetermux-execto 2.4.0, and only then attempt the install — that turns this into the same test path you ran (releasedv2026.6.19.debagainst 2.x), which isolates the variable cleanly. If it still hits the kernel-routing issue on my S918B even with 2.x, then it really is the device-tree/firmware delta. If it works, then the whole "S918B linker64 substitution" theory dies and the answer for all 1.9-stuck-on-F-Droid users is "upgrade termux-exec first."Good data point on the OTA bump too — same kernel, same PT_INTERP behavior — so on your device the kernel line is what matters, not the monthly patch level. Useful contrast for the still-unresolved case where my kernel (
5.15.189, only 11 patches newer than yours but sameandroid13-8branch) might still behave differently.Will report back from the throwaway-prefix test once the mirror swap + reinstall completes.
One more migration gotcha for anyone moving an existing proot
~/.claudeover to the bare setup: any absolute/home/...path that got recorded while claude ran under proot dies in bare — proot's/homeis a bind mount that doesn't exist outside it.The surprising one: plugins all silently "fail to load."
~/.claude/plugins/installed_plugins.json(andknown_marketplaces.json) store each plugin'sinstallPathas/home/.claude/plugins/cache/..., so bare looks at a dead path. The plugin files are fine — only the recorded paths are wrong.Fix — rewrite to the real home (valid under both bare and proot, since proot also sees
/data/data/.../homevia its/databind), then relaunch:(Grep trap when verifying: the real path
.../files/home/.claudecontains the substring/home/.claude, so anchor on the leading quote"/home.)Git worktrees made under proot hit the same thing — their gitdir is recorded as
/home/<repo>/.git/worktrees/..., so git ops fail in bare until you rewrite that path (orgit worktree repair).Net: the bare install is solid; it's the carried-over proot state whose
/homepaths need rewriting. Posting in case it saves someone the "every plugin says failed-to-load" debug.@kingpanther13 great migration intel —
/homepaths leaking out of recorded state is exactly the kind of footgun that's invisible at install time and shows up later as "plugins silently broken." Saving this for anyone who lands here from a proot setup.The
installed_plugins.json+known_marketplaces.json+git worktreesset covers the main offenders; one more I'd add for completeness —~/.claude/projects/<slug>/<uuid>.jsonlitself stores a"cwd"field per session, and/resume's "this conversation is from a different directory" gate compares against it. Your earlier note on session migration covered the slug-rehash; for/resumeto recognize imported sessions on bare, thatcwdfield also needs the rewrite (jqover each.jsonl, orsedif you trust the field's escaping).Not blocking me yet — I'm still upstream of the bare install (S918B's kernel still rejects the patched glibc binary; mirror swap to grab termux-exec 2.x didn't go through cleanly because F-Droid's keyring doesn't carry the modern signing key 5A897D96E57CF20C — reverted and back on the 2.1.112 pin). When I eventually get the bare install working, this
/homerewrite checklist is what I'll run before launching. Thanks again.One more bare-setup footgun, in the same vein as the
/homepath one — this one's a live DNS bug, not migration state.Symptom: on the bare install, WebFetch fails on every URL with "Unable to verify if domain … is safe to fetch … blocking claude.ai." The same root cause also breaks the claude.ai MCP connector and OTEL export (both log
ECONNABORTED/getaddrinfo ETIMEOUT). The main API/session is fine, so it hides until you reach for WebFetch.Cause: the bundled bun ships its own c-ares resolver (for the
node:http/axios paths), and c-ares reads the absolute/etc/resolv.conf. On Android/etc→/system/etchas noresolv.conf, so c-ares has zero nameservers and lookups hang ~10s. The pieces that work do so because they use a different path: Termux's glibc is patched to$PREFIX/glibc/etc/resolv.conf(sogetentresolves fine), and bun's nativefetchhas its own DNS — only c-ares is left pointing at the missing absolute path. Effectively a proot→bare regression (proot provided/etc/resolv.conf).Note:
ipv4first/skipWebFetchPreflightdon't help — it's resolution, not address selection, and the real fetch uses the same c-ares.Workaround: an
LD_PRELOADshim that redirectsopen()/fopen()of/etc/resolv.conf→$PREFIX/etc/resolv.conf, loaded next touname-spoof.so. A/B-validated: baseline fails, shim makes WebFetch + the MCP connector work; Bash tool and startup unaffected. Wired it via a regular-file~/.local/bin/claudewrapper (the launcher'slink-native.shleaves a non-symlink there untouched, so it survives updates).Net: filed upstream at gtbuchanan/claude-code-termux#25 with the C source + repro, since the launcher already owns
LD_PRELOADand is the right place to fix it. Posting here for anyone who gets the bare install running and then hits "WebFetch is broken."---
Addendum — grep/find caveat: apply this as a compiled launcher, not a shell-script wrapper at
~/.local/bin/claude. A script silently breaks Claude'sgrep/find: those re-exec the bundled binary's embedded ugrep/bfs by argv[0] (exec -a ugrep "$CLAUDE_CODE_EXECPATH" …, which falls back to~/.local/bin/claudebecause this setup blanksCLAUDE_CODE_EXECPATH). A#!-script's shebang re-exec drops the custom argv[0], so the binary runs as the normal CLI and dies witherror: unknown option '-G'. The wrapper must be a compiled launcher that doesexecv(current, argv)(preserving argv[0]) and setsLD_PRELOADwith overwrite — replacing the inherited bioniclibtermux-execpreload, which otherwise makes the glibc ugrep fail withinvalid ELF header. Both glibc shims (uname-spoof.so+resolv_redirect.so) preload cleanly for ugrep/bfs. Folded into gtbuchanan/claude-code-termux#25.WebFetchissue fixed in claude-code-termux v2026.7.25The gap has now crossed the "unmaintainable workaround" line — a data point for prioritization.
Posting from a fresh install to quantify where this regression stands today, since the thread is long and the numbers make the case better than any argument:
| | |
|---|---|
| Last version that runs on Termux | v2.1.112 (final JS
cli.jsentry point) || Current published version | v2.1.206 |
| Releases stranded behind the native-binary switch | 94 |
| Time since this issue opened (v2.1.113, mid-April) | ~83 days / ~12 weeks |
Pinning to
@2.1.112was a reasonable stopgap in April. At 94 releases behind it no longer is — that window now includes changes that are feature-flagged by minimum version (e.g. the remote-control /tengu_ccr_bridgebridge gated at ≥ v2.1.139, per the discussion in #71878), so pinned users don't just miss fixes, they get silently locked out of shipping features regardless of what their client can technically run.What the community has already proven, so the team doesn't have to re-litigate feasibility:
install.cjs/cli-wrapper.cjsreferencelinux-arm64-android; only the built binary target is missing (see #72620).gtbuchanan/claude-code-termux(latest v2026.7.25, tracking upstream releases), is currently doing this packaging + patching work — including fixing the downstreamWebFetch/c-ares DNS breakage — for the whole Termux userbase. When users maintain a shadow distribution channel for 3 months, that's demand, and it's demand paired with a demonstrated, tractable fix.The ask hasn't changed — it just needs a decision. Any one of these closes it:
@anthropic-ai/claude-code-linux-arm64-android— a Bionic-compatible or statically-linked build for the target the platform map already names; orcli.jsas a real JS fallback when no native binary matches the platform, instead of the current hard error; orAndroid/Termux is a first-class primary environment for a lot of us coding on tablets and Chromebooks. 54+ 👍 and 60+ comments here is a sustained, well-documented signal with a repro and a working reference implementation. A committed direction from the team — even "option 2, next cycle" — would let this community stop maintaining a fork and get back on the mainline. 🙏
Data point: latest (2.1.207) runs natively on Termux — non-Samsung device, PT_INTERP honored
Adding a device to @salviz / @kingpanther13's PT_INTERP comparison, since so far it's been Samsung-vs-Samsung.
Environment
panther), custom Sultan-based kernel6.1.145(aarch64)termux-exec 2.5.0(split-lib, shipslibtermux-exec-ld-preload.so)PT_INTERP is honored here. The patched
linux-arm64binary execs directly; the kernel does not substitute/system/bin/linker64. So the salviz S23 Ultra (dm3q) failure is looking device/ROM-specific rather than an Android-16 kernel-line regression — this Pixel is a non-Samsung device tree that behaves like kingpanther's Fold5 (q5q), i.e. honors it.Two upstream gotchas from this thread, both worked around in the wrapper I'm using (ferrumclaudepilgrim/claude-code-android):
/etc/resolv.conf, absent on Android, so WebFetch/MCP/OTEL die (per @kingpanther13's June 27 note). Worked around by injectingrequire("dns").setServers(["8.8.8.8","8.8.4.4"])at startup. WebFetch confirmed working.--versionbut crash on full launch (statx→SIGSYS, bunepoll_pwait2→SIGSEGV; the bun#32489 gate) are caught by an--init-onlysmoke test + blocklist before promotion, so a bad release can't brick the install.No
env.LD_PRELOADinsettings.json→ the bundled grep/rg/find work fine (the wrapper's ownunset LD_PRELOADon the main process is sufficient).Net: the native path now reaches latest, not the ~2.1.158 ceiling mentioned earlier in the thread. Still very much hoping for a first-party
android-arm64/bionic target or a restored JS fallback — the community glibc-runner stack works but is one upstream ELF/bun change away from breaking, and it's dead-on-arrival for the PT_INTERP-rerouting devices.New data point on this thread, and a correction relative to a preliminary observation I made earlier today.
Device
mustanggoogle/mustang_beta/mustang:17/CP31.260618.005/15731206— Android 17 QPR beta program build. I am not claiming this observation extends to the current Pixel stable channel.6.6.127-android15-8-ga60691846d2d-ab15430330-4k2026-06-05googleplay.2026.06.21,termux-exec 1.9,glibc-runner 2.0-3,patchelf-glibc 0.18.0(installed viaaptfrom thetermux-glibcrepo)Observed behavior
The error text is Android bionic's dynamic-linker failure format, not glibc's. So bionic loaded the ELF instead of the requested glibc
ld.soat/data/data/com.termux/files/usr/glibc/lib/ld-linux-aarch64.so.1.readelf -lconfirms that is thePT_INTERP, and the file exists (241440 bytes).What I checked, and what I did NOT establish
Additional invocation forms —
env -i(empty env),bash -c "unset LD_PRELOAD; ...",grunwrapper, and direct invocation of the glibcld.sowith--library-path— all reproduced a bionic-format failure (grunand directld.soinvocation abort withCould not find a PHDR: broken executable?). This shows the failure is not caused by anLD_PRELOADvariable being set at exec time.It does not definitively rule out
termux-exec 1.9as a factor, becauselibtermux-execis likely mapped into the invoking shell at process start and would not be unloaded by clearing the child env. Ruling that out cleanly needs a rawSYS_execvehelper launched from a process demonstrably free of the preload. I have not done that.It also does not by itself establish that the Linux kernel selected bionic; the observable effect is that bionic ended up loading the ELF, but the mechanism could be a kernel path, an Android userspace interposition, an SELinux transition, or another intermediary. I am reporting the observable failure, not asserting a mechanism.
Cross-device matrix (as leads for triage, not a controlled comparison)
dm3q, Android 16, kernel5.15.189-android13-8: reproduces this bionic failure (my original filing, still current onSAFZF5).q5q, Android 16, kernel5.15.178-android13-8: reported the wrapper install succeeding (secondhand; exact test methodology on that report not captured here).pantheron a custom Sultan kernel6.1.145with theferrumclaudepilgrimwrapper on native 2.1.207: reported working (secondhand; custom-kernel status is important — this is not stock Pixel 7 behavior).mustang(QPR beta build), Android 17, kernel6.6.127-android15-8: reproduces this bionic failure (firsthand today).Ancillary
getconf PAGE_SIZEreturns4096on both firsthand-broken devices; the glibc binaries haveLOADp_alignof0x10000(64 KB). If any subscriber has suspected a 16 KB page-size migration as the culprit, the two data points I have do not fit that hypothesis.I have not tested Claude Code itself on the Pixel; the tests above are of the loader environment that the
gtbuchanan/claude-code-termuxwrapper depends on. gtbuchanan'sv2026.7.25preflight fires the JS-fallback guidance on both devices, so the wrapper installs cleanly and points the user atnpm install -g @anthropic-ai/claude-code@2.1.112as designed.Happy to run further tests on either device: raw
execvehelper, SELinux AVC scan,dmesgon a userdebug build, small custombinfmt_elftest.Just run a Claude-icon . The rest will take care. If already installed . A linear path to claude will be mentioned there . Allow use of tracks and main-load deployment to Casulize linear data [Propulsion], Aesthetic : propulsion will be live based. If not dotted info . Then curl on curl m. base call . Continue ultimate~. apparition . Read asymptote ' s . If a decent output Is not found , change to server less name then enable route on route sessions to [p.loop(load)] : [If an acting session is redeemed to the claude . It the main store version having a ] connection with already . Termux : [Which is again a container that claude want to leave as part of it [Natural IDE development]] , Termux - will disable but will automatically cache the pysynced() . In binary details . The read out part was left outward incase anything that returned will be validated on local machine via llvm . exist [RT m : [Mbset{Kernel : mini , Ab-claude , Claude-mini}]]