Summary
There is no way for a WFL program to fail on purpose with its own message. No throw/raise/fail keyword exists, there is no stderr channel, and the process exit code cannot be set — so a program that detects a fatal condition can only print to stdout and then deliberately trigger an unrelated runtime error to get a nonzero status.
Reproduction
The behaviour a program wants (PHP: throw new \RuntimeException('Unclosed string at position: ' . $pos)):
store pos as 42
display "jshrink: Unclosed string at position: " with pos
store fatal as 1 divided by 0
Command:
Expected
A documented way to abort with a program-supplied message and a nonzero exit
status — something in the shape of raise "Unclosed string at position: " with pos.
No documentation citation exists — filed as a capability gap, not a broken
runtime. Docs/03-language-basics/error-handling.md documents catching
comprehensively (try at :5, finally at :135, error_message at :192,
typed when <error type> clauses at :260) but never documents raising. Every
error in the docs originates in the runtime. There is no throw/raise/fail
token among the 181 reserved keywords
(Docs/reference/reserved-keywords.md), and no such function in the 190-entry
native registry.
Actual
The catching half works exactly as documented — this is confirmed, not a
complaint. Errors raised inside nested actions propagate, error_message binds,
finally runs, and execution continues:
trying
caught: Division by zero
cleanup ran
continued
The raising half has no form. The closest achievable behaviour is above, which
gives:
jshrink: Unclosed string at position: 42
on stdout, exit code 1, plus the runtime's own unrelated
Runtime errors: ... error[ERROR]: Division by zero on stderr and — unless
debug_report_enabled = false is set in .wflcfg — a <script>_debug.txt
dropped into the current working directory.
Three separate things are missing:
- no way to construct an error with a custom message;
- no way to write to stderr (both
display and print go to stdout via
emit_line, src/stdlib/core.rs:15);
- no way to set the exit code (and
exit/exit program do not terminate the
program at all — filed separately).
Consequence: a CLI written in WFL cannot report a clean diagnostic. It either
exits 0 after printing an error (looks like success to a shell or CI) or exits 1
with a misleading runtime message attached to a line that has nothing to do with
the real failure.
Environment
- wfl --version:
WebFirst Language (WFL) version 26.8.4
- binary: system install
C:\Program Files\wfl\bin\wfl.exe
- commit: c277d8f
- OS: Windows 11 Pro 10.0.26200
- build: release
Also reproduces on the repo build (26.8.2). No .wflcfg in scope for the repro.
Context
Found while porting G:/repos/JShrink/src/JShrink/Minifier.php (a 738-line PHP
JavaScript minifier) to WFL. JShrink raises four distinct fatal errors with
position-bearing messages (Unclosed string at position: N,
Unclosed multiline comment at position: N, Unclosed regex pattern at position: N,
Unclosed character class at position: N), and minify() catches to run cleanup
before rethrowing.
This one shaped the port's CLI contract: the catch/cleanup structure ported
cleanly onto try / when error / finally, but the four throws became
"print the message, then force a division by zero," which is what is in the port
today. It works — message visible, exit nonzero — and it is not stubbed, but it is
a workaround standing in for a missing primitive, and the misleading
Division by zero on stderr is a real wart for anyone reading CI output.
Note this is a natural companion to the exit issue: a raise statement plus a
working program-terminating exit would together close the whole CLI story.
Summary
There is no way for a WFL program to fail on purpose with its own message. No
throw/raise/failkeyword exists, there is no stderr channel, and the process exit code cannot be set — so a program that detects a fatal condition can only print to stdout and then deliberately trigger an unrelated runtime error to get a nonzero status.Reproduction
The behaviour a program wants (PHP:
throw new \RuntimeException('Unclosed string at position: ' . $pos)):Command:
Expected
A documented way to abort with a program-supplied message and a nonzero exit
status — something in the shape of
raise "Unclosed string at position: " with pos.No documentation citation exists — filed as a capability gap, not a broken
runtime.
Docs/03-language-basics/error-handling.mddocuments catchingcomprehensively (
tryat:5,finallyat:135,error_messageat:192,typed
when <error type>clauses at:260) but never documents raising. Everyerror in the docs originates in the runtime. There is no
throw/raise/failtoken among the 181 reserved keywords
(
Docs/reference/reserved-keywords.md), and no such function in the 190-entrynative registry.
Actual
The catching half works exactly as documented — this is confirmed, not a
complaint. Errors raised inside nested actions propagate,
error_messagebinds,finallyruns, and execution continues:The raising half has no form. The closest achievable behaviour is above, which
gives:
on stdout, exit code 1, plus the runtime's own unrelated
Runtime errors: ... error[ERROR]: Division by zeroon stderr and — unlessdebug_report_enabled = falseis set in.wflcfg— a<script>_debug.txtdropped into the current working directory.
Three separate things are missing:
displayandprintgo to stdout viaemit_line,src/stdlib/core.rs:15);exit/exit programdo not terminate theprogram at all — filed separately).
Consequence: a CLI written in WFL cannot report a clean diagnostic. It either
exits 0 after printing an error (looks like success to a shell or CI) or exits 1
with a misleading runtime message attached to a line that has nothing to do with
the real failure.
Environment
WebFirst Language (WFL) version 26.8.4C:\Program Files\wfl\bin\wfl.exeAlso reproduces on the repo build (26.8.2). No
.wflcfgin scope for the repro.Context
Found while porting
G:/repos/JShrink/src/JShrink/Minifier.php(a 738-line PHPJavaScript minifier) to WFL. JShrink raises four distinct fatal errors with
position-bearing messages (
Unclosed string at position: N,Unclosed multiline comment at position: N,Unclosed regex pattern at position: N,Unclosed character class at position: N), andminify()catches to run cleanupbefore rethrowing.
This one shaped the port's CLI contract: the catch/cleanup structure ported
cleanly onto
try/when error/finally, but the four throws became"print the message, then force a division by zero," which is what is in the port
today. It works — message visible, exit nonzero — and it is not stubbed, but it is
a workaround standing in for a missing primitive, and the misleading
Division by zeroon stderr is a real wart for anyone reading CI output.Note this is a natural companion to the
exitissue: araisestatement plus aworking program-terminating
exitwould together close the whole CLI story.