Informix Error -9
-9 Bad file number.
An operating-system error code with the meaning shown was unexpectedly returned to the database server. Look for other operating-system error messages that might give more information. If the error recurs, note all circumstances and contact IBM Informix Technical Support.
Oninit® Troubleshooting Guidance
This is an operating-system error number, not an Informix diagnosis.
The same errno is returned by many different operations, so on its own it says what the operating system refused, not what Informix was trying to do. Informix will usually have reported a more specific error alongside it — in the message log, in the SQL or ISAM error pair, or in the accompanying assert failure — and that error normally defines the real cause far more precisely than the errno does. Find it before diagnosing from this number alone.
This matters less than it used to. Later versions of the engine trap many of these conditions and report them as specific Informix errors naming the operation, the object and the context, so a bare errno in this range is increasingly a sign of an older version, an unusual code path, or a failure early in startup before the better reporting is available. If you are seeing one on a current version, the more specific error is worth looking for even harder.
Operating-System Meaning
errno 9 is EBADF. The official text says "Bad file number"; every current system renders it "Bad file descriptor", which is the more useful phrasing.
An operation was attempted on a file descriptor that is not valid — either it was never open, it has already been closed, or it is open but not in the mode the operation requires (reading from a write-only descriptor, for instance).
EBADF differs from most errors in this range in one important respect: it is not an environmental condition. A missing file, a full disk or a failed device can all happen to correct code. A bad descriptor generally cannot. Something used a descriptor it should not have used, or closed one it did not own. That makes -9 a pointer at code rather than at configuration — which is why the official text sends you to Technical Support rather than offering a remedy.
Distinguish it from the descriptor errors it gets filed with:
| errno | Symbol | Meaning |
|---|---|---|
| 9 | EBADF |
This descriptor is not valid |
| 24 | EMFILE |
This process cannot open any more descriptors |
| 23 | ENFILE |
The system cannot open any more descriptors |
If you are looking at a resource-exhaustion problem, -24 or -23 is what you want. -9 means the descriptor in hand is wrong, which is a different kind of fault entirely.
What This Means in Informix
An Informix client connection is a file descriptor — a socket, a pipe, or shared-memory attachment depending on the protocol. Anything that invalidates that descriptor behind the library's back produces EBADF on the next call, and the two classic ways to do that are both in application code.
Closing descriptors you do not own
Daemonisation code traditionally closes every inherited descriptor:
for (i = 0; i < 1024; i++)
close(i);
If the program has already connected to Informix, that loop closes the connection's socket along with everything else. The connection object still looks valid to the application; the descriptor beneath it is gone, and the next SQL statement fails with EBADF.
This is the single most common source of -9 in an ESQL/C estate, and it is particularly confusing because the failure appears at the next database call rather than at the point where the damage was done.
A C UDR does not daemonise, so this exact pattern does not arise there — but the underlying mistake does. A UDR runs inside a virtual processor and shares the server's descriptor table; closing anything it did not itself open can invalidate a descriptor the engine is using, with consequences well beyond the calling session.
Forking with an open connection
An Informix connection must not be used by both parent and child after a fork(). The child inherits the descriptor, and if either side closes it — or both talk on it — the other's connection becomes invalid. The symptom is EBADF in whichever process next uses it, typically the one that did nothing wrong.
Other origins:
- Using a connection after disconnect — a pooled or cached connection handle retained past
DISCONNECT, or past a network drop that closed the socket - Double close in error-handling paths, where a descriptor is closed on one path and again on cleanup
- A signal handler closing or reusing descriptors while a call is in progress
- A UDR or DataBlade managing descriptors and getting the lifecycle wrong
- Utilities scripted through pipes where a wrapper closes a stream the utility is still writing to
Common Causes
- A close-all-descriptors loop running after the connection was established.
fork()with an open connection, with both processes then using it.- Use of a connection after it was disconnected or dropped.
- Double close in an error path.
- A descriptor open in the wrong mode for the operation attempted.
- A wrapper script closing a stream an Informix utility is still using.
Diagnostic Checks
Establish which descriptor and which operation. strace names both, and on this error that is usually decisive:
strace -f -p "$pid" 2>&1 | grep -n EBADF
# e.g. write(7, ...) = -1 EBADF (Bad file descriptor)
# ^ the descriptor in question
# Solaris / AIX
truss -f -p "$pid" 2>&1 | grep EBADF
Then see what the process actually has open, and whether that descriptor number is among them:
ls -l /proc/"$pid"/fd
lsof -p "$pid"
lsof -p "$pid" | grep -iE 'tcp|sqlexec|informix'
A descriptor number appearing in the strace output but missing from /proc/<pid>/fd is the confirmation that something closed it.
Rule out exhaustion, which is a different error but often suspected first:
ulimit -n
ls -1 /proc/"$pid"/fd | wc -l
cat /proc/sys/fs/file-nr
If those numbers are nowhere near the limit, this is not a resource problem — see -24 and -23 if they are.
Search the application for the two structural causes. Both are greppable:
# close-all loops
grep -rn -E 'close\s*\(\s*[a-z_]*i[a-z_]*\s*\)' /path/to/src
grep -rn -E 'for\s*\(.*close\s*\(' /path/to/src
grep -rn -E 'getdtablesize|sysconf\s*\(\s*_SC_OPEN_MAX|closefrom|daemon\s*\(' /path/to/src
# fork with a live connection
grep -rn -E '\bfork\s*\(|\bdaemon\s*\(' /path/to/src
Where source is not to hand:
strings /path/to/binary | grep -iE 'closefrom|getdtablesize|daemon'
And check the ordering that matters — whether the connect happens before the daemonise:
strace -f -e trace=connect,close,execve /path/to/program 2>&1 | head -60
Solutions / Resolution
- Find the descriptor and what closed it.
straceplus/proc/<pid>/fdanswers this directly; reasoning about it rarely does. - Daemonise before connecting, not after. If the program must close inherited descriptors, do it at startup before any database work. Where that is not possible, close only what you opened, or use
closefrom()from a known-safe base — never a blind loop toOPEN_MAX. - Do not share a connection across
fork(). Connect in the child after forking, or ensure only one side ever uses the connection and the other closes its copy cleanly. This is not a tuning matter — it corrupts protocol state. - Do not reuse a handle after
DISCONNECTor after a dropped connection. Detect the drop and reconnect rather than retrying on the dead handle. - Audit error paths for double close. Set the descriptor to
-1after closing so a second close is harmless and obvious. - Check descriptor mode where the operation is a read or write on something opened one-way.
- If none of these apply and the fault is inside the engine rather than application code, gather the
strace/trussoutput and the message log before escalating — that is the evidence that distinguishes a product issue from a local one.
Examples
A close-all loop after connecting
EXEC SQL CONNECT TO "prod@ol_prod";
...
/* daemonise */
if (fork() > 0) exit(0);
setsid();
for (i = 0; i < getdtablesize(); i++)
close(i); /* closes the Informix socket too */
...
EXEC SQL SELECT COUNT(*) INTO :n FROM orders; /* -9 */
The connection was established first, so the loop destroys it. Nothing in the SQL is wrong and nothing in the server is wrong; the descriptor beneath the connection no longer exists. Moving the daemonisation above the CONNECT resolves it.
$ strace -f -p 4471 2>&1 | grep EBADF
write(7, "\0\1\0\26...", 22) = -1 EBADF (Bad file descriptor)
$ ls -l /proc/4471/fd | awk '{print $9}' | sort -n
0
1
2
Descriptor 7 is in use by the library and absent from the process. That is the whole diagnosis.
Forked children sharing one connection
A batch program that connects, then forks workers:
$ lsof -p 8801 | grep -c TCP
1
$ lsof -p 8802 | grep -c TCP
1
$ ps -o pid,ppid,cmd -p 8801,8802
PID PPID CMD
8801 8800 /informix/bin/batch_worker
8802 8800 /informix/bin/batch_worker
Two processes holding the same inherited socket. Whichever finishes first closes it, and the other gets EBADF on its next statement — usually reported against the process that was doing nothing unusual. Each worker must open its own connection.
A wrapper closing a utility's stream
$ ontape -s -L 0 | head -20
...
Archive failed
system error = 9
head exits after twenty lines and closes the pipe. Depending on timing this surfaces as -9 or as -32, broken pipe. Neither is an ontape fault; the pipeline is truncating its own output.
Platform Note
errno 9 is EBADF on Linux, AIX, Solaris, HP-UX and the BSD-derived systems — stable, so the number is reliable. Only the rendering differs: "Bad file number" is the historic wording carried by this catalogue, "Bad file descriptor" is what you will see on the host.
Descriptor inspection is not portable:
| Task | Linux | Solaris | AIX |
|---|---|---|---|
| Open descriptors | ls -l /proc/<pid>/fd |
pfiles <pid> |
procfiles <pid> |
| Trace syscalls | strace -f |
truss -f |
truss -f |
| Safe bulk close | closefrom(3) |
closefrom(3C) |
loop from a known base |
closefrom() is the portable-in-spirit answer to the close-all-loop problem where it exists, because it closes from a chosen descriptor upward rather than blindly from zero — but it still closes the Informix socket if that socket's number is above the base. Ordering the daemonisation before the connection is the robust fix on every platform.
Related Errors / Related Topics
- -24 — Too many open files, for this process. The error to look at if descriptor counts are near the limit; -9 is about a specific bad descriptor, not exhaustion.
- -23 — Too many open files in the system, the host-wide equivalent.
- -32 — Broken pipe. The same pipeline and
fork()mistakes often produce one or the other depending on timing, so the two are worth reading together. - -4 — Interrupted system call, the other error that points at a program's signal and process handling rather than at its SQL.
A -9 arriving from an application is a defect in that application's descriptor handling far more often than a fault in Informix. If it appears across several unrelated programs on one host, suspect a shared library, a common framework, or a startup wrapper they all inherit.