codex exec fails on Android (Termux): "lock() not supported" in acquire_app_server_startup_lock
Open 💬 6 comments Opened Jun 4, 2026 by bash0816
Problem
When running codex exec on Android (Termux), the following error occurs at startup:
Error: failed to initialize in-process app-server client: lock() not supported
Root cause: acquire_app_server_startup_lock inapp-server-transport/src/transport/unix_socket.rs calls file.lock()
unconditionally. On Android, flock(2) is unsupported on the /data
partition (returns ENOTSUP), which surfaces as "lock() not supported".
Reproduction:
- Environment: Android (
target_os = "android"), Termux (aarch64), Node v24 - Command:
codex exec "echo hello"(inside a git repo) - Error:
Error: failed to initialize in-process app-server client: lock() not supported
Fix
Add #[cfg(not(target_os = "android"))] before file.lock()?;:
- file.lock()?;
+ #[cfg(not(target_os = "android"))]
+ file.lock()?;
The identical guard is already applied in two other places in this codebase:
codex-rs/core/src/installation_id.rs:32-33codex-rs/arg0/src/lib.rs:329-330
The AppServerStartupLock struct still holds the file open for the process
lifetime (UDS path cleanup on drop). Only the advisory lock acquisition is
skipped on Android.
A branch with this fix is available at:bash0816:fix/android-startup-lock-unix-socket
6 Comments
I've put together a fix and pushed it to a branch in case it's useful to anyone with commit access:
arimu1:fix/26277-android-startup-flock(https://github.com/arimu1/codex/tree/fix/26277-android-startup-flock).The branch guards
File::lock()with#[cfg(not(target_os = "android"))]at two startup-path sites:acquire_app_server_startup_lockinapp-server-transport/src/transport/unix_socket.rsresolve_installation_idincore/src/installation_id.rsThe issue text says
installation_id.rsis "already guarded" — it currently is not onmain. Fixing onlyunix_socket.rsmoves the sameENOTSUPcrash to installation-id resolution on the next startup call. Guarding both letscodex execactually reach a working state on Android.Diff: https://github.com/arimu1/codex/compare/main...arimu1:fix/26277-android-startup-flock
Happy to defer to @bash0816 if they want to fold this into their own branch — credit for the diagnosis and original guard idea is theirs.
Thank you @arimu1 — your analysis is correct, and your fix covers exactly the right spot.
We did additional static analysis tracing the full
codex execstartup path and found three locations that need the Android guard (in call order):| File | Line | Called on every
codex exec| Severity ||------|------|------------------------------|----------|
|
arg0/src/lib.rs| ~341 | Yes (first thing inmain()) | Recommended —try_lockis fail-safe so no immediate crash, but should be guarded ||
core/src/installation_id.rs| 32 | Always, unconditionally | Critical — will crash immediately ||
app-server-transport/src/transport/unix_socket.rs| 151 | Yes | Critical — original report |The issue text incorrectly stated that
installation_id.rswas "already guarded" — that was based on checking a local checkout ofrust-v0.133.0where the guard happened to exist, not upstreammain. Apologies for the confusion.Recommended fix for all three:
We are updating our build-time patch to cover all three. Happy to fold your branch into a combined PR if you'd like to collaborate, or defer to you entirely — the diagnosis credit is shared.
Workaround available for Termux users
For Android/Termux users who need a working build right now, a patched wrapper package is available:
Package: @bash0816/codex-termux@0.133.0-1
Source: https://github.com/bash0816/Codex-Termux
This package applies build-time patches at all three affected locations identified in this issue:
| File | Call | Patch |
|------|------|-------|
|
arg0/src/lib.rs~341 |try_lock()|#[cfg(not(target_os = "android"))]guard ||
core/src/installation_id.rs:32 |lock()|#[cfg(not(target_os = "android"))]guard ||
app-server-transport/src/transport/unix_socket.rs:151 |lock()|#[cfg(not(target_os = "android"))]guard |Both
codex execand the interactive TUI (codexwithout arguments) work correctly with this patched build.We look forward to the upstream fix landing so users can use the official package directly.
Following up: I've updated my fix branch — arimu1:fix/26277-android-startup-flock (head b2f0f52) — so it now:
is rebased on current main,
guards all three startup-path sites identified in @bash0816's analysis (arg0/src/lib.rs, core/src/installation_id.rs, app-server-transport/src/transport/unix_socket.rs) with #[cfg(not(target_os = "android"))], and
passes cargo check clean on the three touched crates.
The lock files are still created and held for the process lifetime; only the advisory flock call is skipped on Android. One note: the arg0 site uses try_lock()?, which propagates ENOTSUP on Android — so it's a real crash path too, not just a hygiene fix.
I understand PRs are invitation-only — happy to open one with this diff (14 insertions, 3 files) if a maintainer would like, or feel free to take the branch directly.
Fourth location found in 0.147.0:
thread-store/src/local/writer_lock.rsThe new
thread-storecrate introduced with the persistent-threads feature (present byrust-v0.147.0) has the sameFile::lock()/try_lock()pattern and hits the identical Android failure. This one is a hard crash on everycodex exec, not just at startup — the whole session fails to initialize:Confirmed via
strace -f -e trace=flock,fcntl,openat: the.coordination.lockfile opens successfully, but noflock/fcntlsyscall is ever attempted before the error — consistent withstd::fs::File::lock()'s Android allowlist gap (Rust 1.95.0,library/std/src/sys/fs/unix.rs), not an actual filesystem limitation (flock(1) and Python'sfcntl.flockboth succeed on the same path/device).Two call sites in
codex-rs/thread-store/src/local/writer_lock.rsneed the same treatment as the other three:| Function | Call | Behavior needed on Android |
|---|---|---|
|
WriterLockCoordinator::lock_coordination|file.lock()?| skip via#[cfg(not(target_os = "android"))], same as the other three sites ||
WriterLockCoordinator::acquire|file.try_lock()matched againstTryLockError::{WouldBlock, Error}| treatError(_)as success on Android (proceed without the lock) instead of returningThreadStoreError::Internal|(
remove_stale_thread_locks's owntry_lock()already degrades gracefully — it just warns and skips on anyError, so it doesn't need a guard.)Given the growing number of sites hitting this same gap, it might be worth raising the underlying
File::lock()Android allowlist omission with rust-lang/rust directly, so future call sites in codex (or elsewhere) don't need per-site guards at all.Correction to my comment above:
writer_lock.rsis not new in 0.147.0 — it's byte-identical (same SHA-256) between 0.146.1 and 0.147.0. So the crash isn't caused by new code; something upstream of it (a caller incodex_core::session, a feature default, or a config gate) started actually exercisingWriterLockCoordinator::lock_coordination/acquirein 0.147.0 that wasn't reached in 0.146.1. I confirmed 0.146.1 runscodex execfine and 0.147.0 crashes with the exact error quoted above, but haven't yet traced what flipped between the two versions. Will follow up once that's identified — the underlyingFile::lock()Android gap and the fix shape for the two call sites still stand, just not the "new in 0.147.0" framing.