Algol 68 error handling and assertions
last modified November 14, 2026
An Algol 68 program can fail in three places: the parser can reject the source
before anything runs, a runtime check can stop the program in the middle of a
statement, and a value read from the outside world can be of the wrong shape.
The first is reported by --check, the second by the runtime and
its exit status, and the third can be caught with a handler installed on a
file.
This article shows the exit status of a normal run, a failed run, and a run
that calls stop; it writes assertions that document the
preconditions of a procedure, turns those assertions off for a release build,
recovers from a value error, and checks a program without running it.
Exit statuses
A program that reaches its end returns status 0 to the shell. The status is
the number that a script tests with $?, and it is the only
information that the caller of the program receives when the program does not
print anything.
BEGIN
print(("program finished", newline))
END
The block runs to its end, the interpreter returns, and the shell sees a successful run. The same status is returned by every program that does not stop on an error, whatever it printed along the way.
$ a68g status.a68 program finished $ echo $? 0
A failed runtime check
A subscript outside the bounds of a row is a runtime error. The interpreter prints the offending source line, a caret under the expression, and a message, and the process ends with status 1. The statements after the failing one are not executed.
BEGIN []INT row = (1, 2, 3); print((row[5], newline)) END
The row has three elements and the program asks for the fifth, so the read is rejected before anything is printed. The same status 1 is returned for an arithmetic overflow, a division by zero, a false assertion, and a failed conversion of input. A shell script can test for it without knowing which check failed. The transcripts in this article show the message alone; the interpreter also prints the source line and a caret above it.
$ a68g fail.a68
a68g: runtime error: 1: index out of bounds (detected in [] "SIMPLOUT" collateral-clause starting at "(" in this line).
$ echo $?
1
Assertions
An assertion states a condition that must hold at that point. It is written
ASSERT (condition);, and a false condition stops the program with
the message false assertion. Assertions are for mistakes in the
program, not for bad input: a failed assertion means that a caller broke a
promise that the procedure relied on.
PROC withdraw = (REF INT balance, INT amount) VOID:
(
ASSERT (amount <= balance);
balance -:= amount
);
BEGIN
INT balance := 100;
withdraw(balance, 30);
print(("balance ", whole(balance, 0), newline));
withdraw(balance, 100);
print(("balance ", whole(balance, 0), newline))
END
The procedure promises to subtract an amount that does not exceed the
balance, and the assertion makes that promise explicit. The first call
succeeds and leaves 70. The second call asks for 100 from a balance of 70, so
the assertion fails before the subtraction changes anything, and the final
print never runs. The condition needs its own parentheses; without them
ASSERT amount <= balance is a syntax error.
$ a68g withdraw.a68
balance 70
a68g: runtime error: 1: false assertion (detected in VOID closed-clause starting at "(" in line 2).
Turning assertions off
The interpreter elaborates assertions by default, and
--noassertions removes them for a run. The checks are meant for
development: a release build can skip them when the cost of the tests matters
or when the caller has already validated the data.
The same withdraw.a68 without assertions lets the second call
through, and the balance becomes negative. The program then finishes
normally, with status 0, which shows why an assertion should not be the only
guard for data that comes from a user.
$ a68g --noassertions withdraw.a68 balance 70 balance -30
Stopping a program
stop ends the program immediately. It is useful when a check
deep inside a procedure finds a condition that makes further work
meaningless, and it returns status 0 like a normal run. A program that must
tell its caller about a failure should therefore let a runtime error happen
or have the caller read a message, because stop does not
distinguish success from an early exit.
BEGIN
print(("stopping now", newline));
stop;
print(("this line is not reached", newline))
END
The message before stop is printed, the message after it is not,
and the shell sees status 0. This implementation also accepts
abort and halt, which end the program in the same
way. None of them takes an exit code, so a script that needs a particular
failure status must test the program's output or arrange for a runtime error.
$ a68g stop.a68 stopping now $ echo $? 0
Recovering from a value error
on value error(file, handler) installs a handler for a file. The
handler has mode PROC (REF FILE) BOOL; returning
TRUE tells the interpreter that the error has been handled, so
the read returns and the program continues. The variable that was being read
keeps its previous value, which is why the program below initialises it and
uses a separate flag to notice the failure.
PROC read in range = (INT lo, hi) INT:
(
INT n := lo;
BOOL bad := FALSE;
on value error(stand in, (REF FILE f) BOOL: (bad := TRUE; TRUE));
WHILE
bad := FALSE;
get(stand in, (n, newline));
bad OR n < lo OR n > hi
DO
print(("expected a number from ", whole(lo, 0), " to ",
whole(hi, 0), ", try again", newline))
OD;
n
);
BEGIN
print(("Enter a number from 1 to 6:", newline));
INT n = read in range(1, 6);
print(("accepted ", whole(n, 0), newline))
END
The loop clears bad, reads a line, and repeats when the handler
set the flag or when the number is outside the range. The handler returns
TRUE after recording the failure, so the read does not stop the
program. The OR is evaluated on both sides, but n is
always defined because it starts at the lower bound. With the input 9, then
abc, then 4, the first value is out of range, the second is not a
number, and the third is accepted.
$ printf '9\nabc\n4\n' | a68g readrange.a68 Enter a number from 1 to 6: expected a number from 1 to 6, try again expected a number from 1 to 6, try again accepted 4
Checking syntax without running
--check parses a source file and stops before execution. It
prints nothing and returns 0 when the file is valid, and it prints the
diagnostic and returns 1 when the parser rejects it. A test script can use the
status to decide whether to run the program at all.
The first file is a small program that sums the numbers from 1 to 10. The second, shown below, has an initialiser with no value.
BEGIN INT total := ; print((total, newline)) END
The declaration has an := with no expression on its right, so
the initialiser series cannot be completed. The parser reports the line of
the assignment and the construct it was trying to build; no part of the
program runs, so even the declaration before the error has no effect.
$ a68g --check good.a68 $ echo $? 0 $ a68g --check broken.a68 a68g: syntax error: 1: construct beginning with "BEGIN" in line 1 followed by an initialiser-series starting in line 2 and then ":=", ";", a specification in line 3, "END" in line 4 is not a valid serial-clause. $ echo $? 1
Next steps
The reading input article covers the handlers in the context of prompts and sentinel loops, and the environment and processes article reads the exit status of another program. The big integers article shows the overflow that one of these runtime checks reports, and files explains the file values that the handlers receive.
Source
Revised Report on the Algorithmic Language Algol 68
In this article we have compared the exit status of a normal run, a failed
runtime check, and a call to stop; written assertions and turned
them off; recovered from a value error; and checked a program without running
it.
Author
List all Algol 68 tutorials.