Informix Error -101
-101 ISAM error: file is not open.
The program attempted to use an unopened file, table, partition, tablespace, or other storage object, or one of these whose access mode did not support the requested operation (for example, an attempt to write to a file that was opened in read-only mode).
If the error recurs, refer to the information on trapping errors in your Administrator's Guide or Reference for additional diagnostics. Contact IBM Informix Technical Support with the diagnostic information.
Oninit® Troubleshooting Guidance
Reasons / Common Causes
-101 is the ISAM-level complaint that a call was made against a file, table, partition, or other storage object that the ISAM processor does not currently consider open. Like -100, it is reached either directly (a C-ISAM program calling the ISAM API itself) or indirectly (the SQL engine's own internal use of the same library), and the literal condition is always one of two things: the descriptor was never successfully opened, or it was open once and has since been closed out from under the caller.
The realistic paths to that state:
- An
isopen()/isbuild()call failed and the return value wasn't checked. Both return a negative file descriptor on failure. Code that doesn't test for that and proceeds to callisread()/iswrite()/etc. against the "descriptor" is operating on a value that was never a valid open file in the first place. isclose()was already called on that descriptor, and something later reuses it believing the file is still open — a double-close-then-reuse bug, or two code paths that each think they own the lifetime of the same handle.- Ownership of the descriptor is split across modules with no single owner. One function opens a file and hands the integer descriptor around; another function, unaware anything else is still using it, closes it once it's done with its own portion of the work.
- A multi-threaded C-ISAM or ESQL/C program without per-thread isolation. The ISAM file table is process-global, not per-thread — if one thread closes a descriptor number, every other thread holding that same integer is now holding a dead reference, whether or not it knows the close happened.
- Concurrent DDL against the same table. A
DROP TABLE,RENAME TABLE, or a completingALTERfrom another session can invalidate a table's open storage objects while your session still holds a cursor or an in-flight operation against the old definition. - The underlying dbspace or chunk went offline between when a cursor was opened and when a later fetch or write runs against it — a chunk marked down, or a dbspace drop, removes the storage object out from under any session still referencing it. Engines sometimes report this generically as "not open" rather than surfacing the more specific chunk/dbspace-down error.
- A restart or checkpoint window raced application startup. Code that issues an ISAM call before the engine has finished bringing a table's storage back online (a rolling restart, a fast recovery still in progress) can hit a descriptor that isn't open yet, which looks identical to one that's been closed.
- A silently-failed open due to OS resource exhaustion (file descriptor limits,
ulimit -n) that wasn't reported through the ISAM library's own "too many files open" path (-104) — rare, but worth ruling out when -101 appears without any of the above being reproducible.
Solutions / Resolution
- Check the return value of every
isopen()/isbuild()call immediately, before issuing any further operation against the result. Treat a negative return as fatal for that code path — don't fall through and callisread()/iswrite()on it regardless. - Pair every successful open with exactly one close, and invalidate the local variable
holding the descriptor the moment
isclose()runs, so later code can't accidentally reuse a stale integer as though it still pointed at an open file. - Give each file handle a single owner. If more than one function needs to operate on the same open file, pass the already-open descriptor down to them rather than letting each function open and close it independently — independent lifetimes on a shared resource is exactly the setup that produces this error.
- In multi-threaded code, serialize opens and closes, or keep per-thread file tables instead of relying on the process-global ISAM file table implicitly staying consistent across threads.
- If concurrent DDL is the suspect, confirm it: check
systablesfor the table's current creation timestamp against the time your session's cursor was opened, and review DBA change logs or application audit logs for aDROP/RENAME/ALTERin the same window. The fix is usually process (coordinate schema changes with active sessions), not code. - If a dbspace or chunk going offline is the cause, that's an availability problem, not an application bug — see Diagnostic Checks below for confirming chunk/dbspace status, and involve whoever manages storage before assuming the application is at fault.
- If a restart/checkpoint window is a known factor in your environment, add explicit retry-with-backoff around startup-time ISAM calls rather than assuming the engine's storage is immediately available the instant the process itself is reachable.
- Rule out OS-level file descriptor exhaustion — compare
ulimit -nfor the server process against the number of open tables, indexes, and partitions it actually needs, particularly after adding fragments or indexes, and raise the limit if it's close to the ceiling.
Examples
The unchecked open
/* C-ISAM: isopen() failure is silently ignored */
int fd = isopen("customer", ISINPUT);
/* fd is negative here — isopen() failed — but nothing checks it */
if (isread(fd, &record, ISNEXT) < 0) {
if (iserrno == -101) {
/* "file is not open" — because it never successfully opened at all */
}
}
The fix is not in the isread() call — it's the missing check three lines earlier. By the time
-101 appears, the actual failure (why isopen() returned negative) has already happened and its
own error code has been overwritten.
The double close
int fd = isopen("orders", ISINPUT);
process_order_batch(fd);
isclose(fd); /* module A is done, closes its handle */
/* ... later, in a different function that still holds the same fd value ... */
generate_summary_report(fd); /* uses the same integer, unaware it was closed */
/* -101 on the first ISAM call generate_summary_report() makes */
Nothing about generate_summary_report()'s own code is wrong in isolation — it received a file
descriptor and used it. The bug is the shared, unsynchronized lifetime: two pieces of code each
believed they had a legitimate reason to treat the same integer as "still open."
Concurrent DDL under a live cursor
-- Session A -- Session B
DECLARE cur1 CURSOR FOR
SELECT * FROM staging_table;
OPEN cur1;
FETCH cur1 INTO :rec; -- succeeds
DROP TABLE staging_table;
-- succeeds; A's session
-- was not holding a lock
-- that would block this
FETCH cur1 INTO :rec; -- -101: the storage object
-- this cursor pointed at no longer exists
Session A's cursor was valid when opened; nothing in its own code changed. The table's storage was removed out from under it by unrelated, independently-successful DDL in another session.
Diagnostic Checks
- Audit the code path for unchecked
isopen()/isbuild()returns — this is the single most common root cause and the cheapest to rule out. Look specifically for any ISAM call whose return value feeds directly into a later call without an intervening test. - Check for a prior, successful
isclose()on the same descriptor earlier in the same session or process — search logs or add temporary tracing around everyisopen()/isclose()pair if the code doesn't already log them. - For a suspected dbspace/chunk issue, confirm chunk status directly:
and check the online message log for chunk-down or dbspace-drop events around the failure time.onstat -d - For suspected concurrent DDL, compare timestamps:
ASELECT tabname, created FROM systables WHERE tabname = 'staging_table';createdtime later than when your session opened its cursor confirms the table was dropped and possibly recreated while you held a reference to the old one. - For multi-threaded C-ISAM/ESQL programs, check whether ISAM calls from different threads ever operate on the same descriptor number without a lock protecting opens/closes — this is a design review, not something a log will show directly.
- For OS-level exhaustion, compare the process's open file count against its limit:
A count near the limit, especially rising over time, points at resource exhaustion rather than an application logic error.lsof -p <oninit pid> | wc -l ulimit -n
Related Errors / Related Topics
- -100 — "ISAM error: duplicate value for a record with unique key." The other foundational ISAM-level error reached the same two ways (direct ISAM API use, or as the SQL engine's own internal plumbing) — worth reading together to understand how this error class surfaces differently depending on the caller.
- -104 — "ISAM error: too many files open." A resource-exhaustion condition that can produce effects resembling -101 (an open that silently didn't happen) when the OS-level file descriptor ceiling is the actual constraint rather than any of the ownership/lifetime issues above.
If you're seeing -101 without any of the ownership/lifetime bugs above being reproducible in the code, check dbspace/chunk availability and concurrent DDL first — both remove a storage object out from under a session that did nothing wrong itself.