DOS INT 21h Function 59h: Read Extended Error Diagnostics
Capture DOS error class, suggested recovery, and locus immediately after a failed call, while preserving registers and avoiding stale diagnostics.
A failed DOS call often exposes only a compact status: a carry flag and an error number for handle-based services, or a small status in AL for some older interfaces. INT 21h/AH=59h, Get Extended Error, can supply additional diagnostic context: an extended error code in AX, an error class in BH, a suggested action in BL, and an error locus in CH.
The tuple helps a program distinguish cases that collapse into a similar older error result. For example, a general “access denied” result may be associated with a sharing conflict, a read-only target, an authorization problem, or another cause. Extended-error information is diagnostic guidance, not a complete root-cause trace: the class groups errors, the locus gives a broad area, and the suggested action is not a guaranteed recovery plan.
The call contract and its timing rule
Function 59h is available in MS-DOS 3.0 and later. On entry, set AH=59h and BX=0000h, the current error-information level. On return, AX contains the extended error code, BH the class, BL the recommended action, and CH the locus. Preserve any other registers your caller still needs; the function clobbers more registers than it returns useful data in.
Most importantly, call it immediately after the failed DOS operation. The extended record describes the preceding DOS call, and another DOS call in between may replace or destroy the evidence. Do not print a message with INT 21h/AH=09h, close a file, query the clock, or ask DOS for another property before collecting the error tuple. Save the original function’s result first, then query extended diagnostics, copy the output fields to memory, and only then perform cleanup or user-visible output.
; The preceding DOS operation has just returned an error.
; Save its simple result before Function 59h replaces AX.
mov [original_error], ax
push ds ; Function 59h may destroy DS
mov ah, 59h
xor bx, bx ; error-information level 00h
int 21h
pop ds
mov [extended_code], ax
mov [error_class], bh
mov [suggested_action], bl
mov [error_locus], ch
; Now it is safe to call DOS for formatting, cleanup, or output.
The sample shows the order of operations, not a complete assembly program. Its storage must be in memory the program owns, the original error result must be captured according to the preceding function’s return convention, and register/segment preservation must follow the compiler or assembler calling convention. In particular, do not use AX from Function 59h as though it were still the simple error returned by the failed call; the two codes answer different questions.
Read the fields as a set, not as a single magic number
AX is the most specific of the four outputs, but the value space evolved as DOS added features. Later versions added new error codes, and DOS systems may map newer conditions to older return codes so existing software continues to work. Treat the tuple as an extensible interface: recognize codes you understand, keep a generic fallback for unknown ones, and do not assume an exhaustive table written for one DOS release covers every compatible kernel or redirector.
BH classifies the nature of the failure at a broad level. The MS-DOS reference includes categories such as resource exhaustion, temporary conditions, authorization problems, hardware failures, application errors, missing files, invalid formats, locked resources, and media errors. These categories support a different recovery policy, but they are not a substitute for the specific code in AX.
BL is a suggested response, such as retrying, pausing before retry, requesting corrected user input, terminating with cleanup, or prompting the user to take corrective action. Microsoft explicitly describes these codes as suggestions. A storage operation that returns “retry” may still fail repeatedly; an application must impose a retry limit, avoid busy loops, and make non-idempotent operations safe before retrying them. Never translate the action byte directly into an unbounded loop or assume that “retry” means the original request had no side effects.
CH reports a broad locus: unknown, block device, network, serial device, or memory-related. It can help decide which subsystem to mention in logs or which diagnostic to collect next. It does not identify a drive model, network host, printer port, or faulty RAM address. Keep messages honest: “DOS reported a block-device locus” is supportable; “the disk hardware is defective” is not implied by the field alone.
Example: preserving a useful diagnostic record
An error logger should preserve two layers: the original call’s status and the extended tuple. The Microsoft KB gives this example extended tuple for a sector-not-found condition:
extended result: AX=0027h, BH=0Bh, BL=04h, CH=02h
meaning: sector not found / media / terminate with cleanup / block device
That documented example explains the tuple as sector-not-found, media error, terminate with cleanup, and block-device locus. It is not a promise that every DOS returns the same tuple for every failure. A useful record also includes the DOS/kernel version, requested path, access mode, current drive, and whether a network redirector is involved, while excluding passwords and sensitive file contents.
Extended errors are also useful for distinguishing compatibility-level results from the underlying condition. DOS 2.x programs commonly saw a smaller set of errors than later systems. New conditions could be mapped to an older code for an existing caller; Function 59h exists to expose richer information to software that can use it. This makes error handling more informative without requiring every old caller to understand newer codes.
Preserve the original context and avoid stale state
The call changes registers beyond AX, BH, BL, and CH. If the failed program needs the original path pointer, data segment, buffer address, or loop counter, save those values before making the diagnostic call. This is especially important in assembly-language error paths, where a message-printing routine may depend on DS:DX and the failing file routine may have already returned different values in the same registers.
Do not call Function 59h after a successful DOS call and interpret the result as though it belongs to an earlier failure. The query is about the most recent DOS error context, not a durable per-handle error object. Copy the fields to application-owned memory before invoking any cleanup operation that may call DOS. If extended-error reporting is unavailable, returns an unknown code, or provides no useful detail, fall back to the original function’s status.
DOS documentation also allows a user-written INT 24h critical-error handler to use Function 59h to inspect the error that caused it to run. That is a specialized handler context, not permission to perform arbitrary DOS work there. Critical-error handlers have strict constraints around reentrancy and available services; collect only what the environment permits and defer complex logging or recovery until a safe point.
A practical recovery policy
Treat the diagnostic as an input to an explicit policy:
- Capture the failed function’s own status and preserve needed registers.
- If the target supports Function 59h, query it before making another DOS call.
- Store the tuple and the operating context in application-owned memory.
- Use known extended codes and class/action/locus values to choose a bounded, operation-safe recovery path.
- Fall back to the original result when the tuple is absent or unknown.
- Report what DOS actually returned, without claiming that a broad locus identifies a physical root cause.
For FreeDOS and other DOS-compatible systems, test this behavior on the exact kernel, redirector, and storage or device stack the program targets. The API is designed for compatibility, but availability, extended-code coverage, and optional network details can vary. Correctly checking the original error, querying at the right time, preserving registers, and retaining a conservative fallback make the feature useful without making the application depend on one version’s internal error table.
Related:
- DOS File Handles: Open, Read, Inherit, and Redirect I/O
- FreeDOS INT 21h IOCTL: Device Status and Control Requests
Sources: