Errors, refusals & diagnostics¶
Programs fail in different ways. A misspelled name is not the same event as a full disk, and “record not found” is not the same event as malformed bytecode. D/BASIC keeps those distinctions visible so recovery code does not have to guess what happened.
The three moments of failure¶
| Moment | Example | What happens |
|---|---|---|
| compile | unknown name, wrong type, unmatched block | no DBC image is produced; fix the source |
| load/admission | missing capability, incompatible DBC version, unresolved library | program code does not begin |
| runtime | bounds, capacity, stale handle, refused Intent, explicit ERROR |
active ON REFUSAL handler runs, or the program stops with the refusal |
Compile and admission failures cannot be caught by the program that never started. Runtime refusals can be handled only when the program has a truthful alternative.
Catch a runtime refusal¶
ON REFUSAL GOTO CouldNotLoad
CALL LoadOptionalReport
PRINT "REPORT READY"
GOTO Done
CouldNotLoad:
PRINT "REPORT UNAVAILABLE. RULE "; ERR; " LINE "; ERL
RESUME Done
Done:
END
ON REFUSAL GOTO target establishes the handler. The target may be a named
label or numbered line. A handler should either finish the current job or use a
RESUME form; falling through and pretending the failed operation succeeded is
almost always a bug.
The handler is program-wide, not a lexical try block. Refusal handling is
also deliberately non-reentrant: if an operation inside the active handler
refuses while the first refusal is still in flight, execution terminates rather
than recursively entering the same handler. Keep handler work small and make
its cleanup operations safe to repeat.
Inspect ERR and ERL¶
| Value | Meaning |
|---|---|
ERR |
stable nonzero rule identifier for the refusal in flight |
ERL |
associated numbered source line, or 0 when no numbered line is available |
Outside a refusal handler, both values are empty observations (0). Treat the
rule identifier as data: log it, compare it when a particular recovery really
depends on it, and include it in a useful bug report. Do not replace it with a
fragile comparison against prose produced by one host.
Resume deliberately¶
| Form | Meaning |
|---|---|
RESUME NEXT |
continue after the refused operation |
RESUME target |
leave the handler at a named label or numbered line |
Prefer an explicit target when the failed operation would have established state needed by the next statement:
ON REFUSAL GOTO NoOptionalSound
CALL OpenOptionalSound
SoundReady% = -1
GOTO ContinueSetup
NoOptionalSound:
SoundReady% = 0
RESUME ContinueSetup
ContinueSetup:
PRINT "SETUP COMPLETE"
RESUME NEXT is best for a self-contained optional action whose following code
does not assume a result. It is not a general “ignore errors” switch.
Raise a governed rule intentionally¶
ERROR raises one literal nonzero rule ID:
The operand is a numeric literal in 1..65535; a variable or CONST name is
not accepted there. ERR is an INTEGER, so it exposes the same 16 rule bits
as a signed value: &HF001 displays as -4095. Compare a private rule with the
same hexadecimal literal—IF ERR = &HF001 THEN—rather than decimal 61441.
Syntax acceptance does not allocate ownership of that number. A rule that can
reach a shipping image must be registered in D/BASIC's append-only rule
register and the literal must name exactly that governed condition.
The private experimental band is 61440..65534 (&HF000..&HFFFE). It is useful
while designing a local invariant, but it must not appear in a shipped image or
published conformance trace. Move the condition to its allocated registered ID
before release. 0 means no refusal and 65535 is never allocated.
Use ERROR for an impossible or explicitly refused state, not for ordinary
branching and not as an ad-hoc substitute for a result value.
Expected outcomes are often values, not refusals¶
A well-designed external operation distinguishes an expected result from a broken contract:
- FILE
STATmay report that a name is absent as a normal status; - a DATA query may return zero rows;
- a network response may carry an HTTP status;
- a Save transaction may still be pending; and
- cancel may be an ordinary terminal outcome.
Those belong in result records and application logic. Malformed carriers, stale bindings, unavailable required capabilities, invalid bounds, and violated integrity rules are refusals. Catching every non-success as one undifferentiated error would throw away information the user needs.
Cleanup is part of the control flow¶
Anything opened or retained should have one visible cleanup path:
DIM SessionOpen AS INTEGER
ON REFUSAL GOTO Failed
CALL OpenSession
SessionOpen = -1
CALL DoWork
GOTO Cleanup
Failed:
PRINT "WORK REFUSED. RULE "; ERR
RESUME Cleanup
Cleanup:
IF SessionOpen THEN CALL CloseSession
END
For owner objects, put idempotent close/cancel behavior in a method and call it
from both the normal path and the window's CLOSE face. Destructors remain a
last deterministic release point, not a substitute for reporting whether an
external commit succeeded.
Do not catch what you cannot repair¶
Good reasons to install a handler:
- an optional capability has a real fallback;
- a transaction can be cancelled and the prior document retained;
- a user can choose a different file or retry after freeing space;
- a GUI can keep its last verified model visible; or
- a console tool can add context before finishing.
Bad reasons:
- hiding a type or bounds bug;
- continuing with output from an operation that never completed;
- converting every refusal to “not found”; or
- looping forever on a capability the host has explicitly refused.
Reading compiler diagnostics¶
A useful diagnostic answers four questions:
- Where? File, line, and column or source span.
- What? The name, type, shape, or construct that could not be accepted.
- Why? A stable code or governing rule.
- What next? The nearest valid spelling or architectural boundary.
Fix the first diagnostic first; later errors may be consequences of the same missing token. In the playground, selecting a diagnostic moves the source caret to its position. In command-line work, preserve the stable diagnostic code in automation and show the human explanation to the person running the build.
Errors in GUI applications¶
Never spin, retry, or open a modal alert from DRAW. Store the terminal outcome
in the model, request invalidation, and let the next frame present it. Keep
Cancel and Close reachable while an external operation is pending. A useful
status surface says what failed and what the person can do—not merely “Error.”
SUB ReportFailure (BYVAL Rule AS INTEGER)
StatusText$ = "COULD NOT SAVE. RULE " + LTRIM$(STR$(Rule))
SERVICE INVALIDATE()
END SUB
A refusal checklist¶
- Decide whether the condition is an expected result or a refusal.
- Catch only when a real recovery or cleanup path exists.
- Read
ERRandERLbefore leaving the handler. - Never execute success-only code after a refused operation.
- Resume to an explicit safe point when state may be incomplete.
- Keep the last verified user-visible state authoritative.
- Close or release every owned session on success, cancel, and refusal.
- Preserve stable rule identifiers in tests and reports.