Linux system errors
Bad File Descriptor
Reviewed for reference consistency: August 11, 2026
the descriptor number is not open in this process's table
What 9 Means
The 9 error on the Linux system errors indicates bad file descriptor — the descriptor number is not open in this process's table. This typically occurs due to using a descriptor after closing it (a use-after-close bug, often a double close).
Exit code 9 corresponds to EBADF, 'Bad file descriptor'. Every process keeps a small numbered table of open files, sockets, and pipes; EBADF is what the kernel answers when a syscall asks for a row that is not there. The number may never have been opened, or it was open once and something closed it behind the caller's back.
How to fix 9
General informational guidance, not professional advice. Commands can affect your system or data — back up first and proceed at your own risk. FixerCode is an independent reference, not affiliated with any vendor mentioned.
List the shell's open descriptors
The per-process fd directory shows every open descriptor as a numbered entry, a direct view of the table EBADF checks against.
ls -l /proc/self/fd/Map descriptors to their targets
lsof names what each descriptor actually is — file, socket, or pipe — for any process being investigated.
lsof -p $$
Technical Background
Every process keeps a numbered table of open files; EBADF means the number asked for has no row. The check is purely bookkeeping — no permission was denied, no device misbehaved, no disk filled. The kernel looked up the handle, found nothing, and refused the operation on the spot.
Two bugs produce nearly all real cases. The first is lifetime: code closes a descriptor and keeps using the number, or closes it twice, or a library closes it while the caller is not looking. The second is provenance: a descriptor from a failed open — usually -1 — flows into a syscall as though it were valid.
The descriptor table is directly observable per process, which makes EBADF unusually debuggable: the numbers are listed, and each entry names what it points at. Comparing the table against the descriptor a program believes it owns usually ends the investigation.
Common Causes
- Using a descriptor after closing it (a use-after-close bug, often a double close)
- A library closing a descriptor while the caller still holds the number
- A descriptor value passed that was never opened or came from a failed open
Typical Scenarios
- A C program closing a file descriptor twice, the second close returning EBADF
- A library abstraction silently closing a socket while application code keeps writing to the number
- A shell redirection referencing a descriptor number that was never duplicated in the first place
What to Know
The practical check is the process's own descriptor directory: what is open, and does the failing number exist there. When the number is missing, lifetime tracing — who closed it and when — is the remaining question.
Frequently Asked Questions
Common questions about Linux 9 error
EBADF (exit 9) is a failed table lookup — the descriptor number has no row. EFAULT (exit 14) is a failed memory access — the buffer a descriptor points to is unreachable. One is about the handle, the other about the address.
close() frees the number for reuse; the next open in the process may claim it. Code holding the stale number then operates on the wrong file — and the second close of the original number can even target the new one.
The per-process fd directory lists every open descriptor as a numbered entry, and lsof maps the numbers to what they actually are — both work on any running process you can inspect.
Related Error Codes
Bad Address — the kernel could not reach the user-space buffer a syscall pointed at
Invalid Argument — a syscall received a malformed or self-contradictory parameter
Too Many Open Files — the process hit its per-process descriptor ceiling
Broken Pipe — a write failed because the pipe's read end had already closed
Related Errors From Other Categories
Similar error codes documented across different platforms and systems
Git paused the merge because it cannot automatically resolve overlapping changes.
Git aborted the merge to protect uncommitted changes in your working directory.
Git prevented a branch switch because it would overwrite unsaved changes in your working directory.
Git aborted the merge because it would overwrite untracked files in your working directory.