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 in
app-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-33
  • codex-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

View original on GitHub ↗

6 Comments

arimu1 · 2 months ago

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_lock in app-server-transport/src/transport/unix_socket.rs
  • resolve_installation_id in core/src/installation_id.rs

The issue text says installation_id.rs is "already guarded" — it currently is not on main. Fixing only unix_socket.rs moves the same ENOTSUP crash to installation-id resolution on the next startup call. Guarding both lets codex exec actually 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.

bash0816 · 2 months ago

Thank you @arimu1 — your analysis is correct, and your fix covers exactly the right spot.

We did additional static analysis tracing the full codex exec startup 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 in main()) | Recommended — try_lock is 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.rs was "already guarded" — that was based on checking a local checkout of rust-v0.133.0 where the guard happened to exist, not upstream main. Apologies for the confusion.

Recommended fix for all three:

#[cfg(not(target_os = "android"))]
file.lock()?;   // or try_lock — same pattern

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.

bash0816 · 2 months ago

Workaround available for Termux users

For Android/Termux users who need a working build right now, a patched wrapper package is available:

npm install -g @bash0816/codex-termux@latest
codex --version

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 exec and the interactive TUI (codex without arguments) work correctly with this patched build.

We look forward to the upstream fix landing so users can use the official package directly.

arimu1 · 1 month ago

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.

bash0816 · 18 days ago

Fourth location found in 0.147.0: thread-store/src/local/writer_lock.rs

The new thread-store crate introduced with the persistent-threads feature (present by rust-v0.147.0) has the same File::lock()/try_lock() pattern and hits the identical Android failure. This one is a hard crash on every codex exec, not just at startup — the whole session fails to initialize:

ERROR failed to acquire thread writer coordination lock .../.coordination.lock: lock() not supported
Error: thread/start: thread/start failed: error creating thread: Fatal error: Failed to initialize session: thread-store internal error: ... lock() not supported (code -32603)

Confirmed via strace -f -e trace=flock,fcntl,openat: the .coordination.lock file opens successfully, but no flock/fcntl syscall is ever attempted before the error — consistent with std::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's fcntl.flock both succeed on the same path/device).

Two call sites in codex-rs/thread-store/src/local/writer_lock.rs need 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 against TryLockError::{WouldBlock, Error} | treat Error(_) as success on Android (proceed without the lock) instead of returning ThreadStoreError::Internal |

(remove_stale_thread_locks's own try_lock() already degrades gracefully — it just warns and skips on any Error, 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.

bash0816 · 18 days ago

Correction to my comment above: writer_lock.rs is 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 in codex_core::session, a feature default, or a config gate) started actually exercising WriterLockCoordinator::lock_coordination/acquire in 0.147.0 that wasn't reached in 0.146.1. I confirmed 0.146.1 runs codex exec fine 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 underlying File::lock() Android gap and the fix shape for the two call sites still stand, just not the "new in 0.147.0" framing.