Watch at EaseStories. Answers. Perspective.

Technology · PRACTICAL GUIDE

Linux says “Text file busy”? Identify the executing inode before overwriting or retrying

ETXTBSY can mean a deployment tried to write a running executable, or a launcher tried to execute a file still open for writing. Preserve the failing syscall, process, inode and namespace before killing anything; then repair the race or replace the pathname atomically.

Diagram of a protected running inode and a blocked in-place overwrite
Original illustration: prove the running inode before changing a pathname. Watch at Ease

ETXTBSY can mean a deployment tried to write a running executable, or a launcher tried to execute a file still open for writing. Preserve the failing syscall, process, inode and namespace before killing anything; then repair the race or replace the pathname atomically.

execve() can return ETXTBSY when the executable is open for writing. A write or truncate can be rejected while the same inode is being executed.

1. Preserve direction, pathname and inode

Record the full error, timestamp, command, PID, container or mount namespace and the exact operation that failed. Distinguish exec from open, truncate, copy or rename; the same words do not prove the same direction. Save device and inode with stat before a restart changes the evidence.

Do not start with reboot, pkill -f or a broad retry loop. A pathname can name a different inode after replacement, and a container may not see the host process holding the object. Compare device and inode from the failing namespace, not just matching text paths.

2. Prove the executor or writer in the right namespace

For a suspected running executable, inspect /proc/<pid>/exe and a small fuser -v or equivalent process report. The executable marker and the open-for-write marker answer different questions; verify permissions and avoid publishing unrelated command lines or paths.

For an exec failure, find the process that still owns a writable descriptor and inspect its descriptor target and flags. Include parent and child processes because a writer opened without close-on-exec can survive through a concurrent spawn. Stop only the proven owner or close the descriptor through the application.

3. Repair the write-then-exec race instead of hiding it

Reproduce the smallest sequence: create a sibling file, write completely, set its mode, close it, then execute it. Under parallel load, serialize cache extraction or use per-version temporary paths so two workers never populate and execute the same target concurrently.

Fix descriptor lifetime at the source and use close-on-exec where appropriate. A short retry keyed only to ETXTBSY may reduce a residual race, but it must be bounded and observable; it cannot replace closing the writer or eliminating shared-path extraction.

4. Publish a sibling and atomically replace the pathname

Never truncate a live executable in place. Write a new sibling on the same filesystem, set owner and mode, verify its hash and test it directly, then rename it over the destination. Same-filesystem rename provides the atomic pathname handoff; existing processes continue through the old inode.

If crash durability matters, also use the application’s required file and directory synchronization sequence. Restart or reload only the controlled service after the rename, and keep the old artifact addressable for rollback rather than deleting evidence immediately.

5. Accept recovery with process and inode evidence

Confirm the new PID resolves through /proc/<pid>/exe to the intended pathname and expected device, inode and content hash. Verify the old PID, if intentionally left running, still maps to the old inode until drained.

Run concurrent write-and-launch tests that previously triggered ETXTBSY. Acceptance requires no busy error, no lingering writable descriptor on the executable, correct permissions and hash, and a tested rollback. If any gate fails, restore the previous pathname and reopen the race investigation.

Diagram of a verified sibling file moving through an atomic rename
Original illustration: validate a new sibling, atomically rename it, then drain the old inode. Watch at Ease