Summary
count loops abort at runtime after 10,001 iterations — but only when the end value is ≤ 1,000,000. A loop with a larger end value runs uncapped, so count from 1 to 20000 fails while count from 1 to 1000001 succeeds. The guard is keyed on the end value rather than on the iteration count, and it protects the small loops while abandoning the large ones.
Reproduction
store total as 0
count from 1 to 20000:
change total to total plus 1
end count
display "total: " with total
Command:
Expected
total: 20000, exit 0.
Docs/03-language-basics/loops-and-iteration.md:5-29 documents the construct as
count from <start> to <end> with no stated bound ("The count loop repeats a
specific number of times"), and :274 uses count from 1 to 100 as a plain
example. No documentation anywhere mentions an iteration cap —
grep -rniE "maximum iterations|iteration limit|10001|10000 iterations" Docs/
returns zero hits.
Actual
Runtime errors:
error[ERROR]: Count loop exceeded maximum iterations (10001)
┌─ g6_count_cap.wfl:4:10
│
4 │ end count
│ ╭─────────^
5 │ │ display "total: " with total
│ ╰^ Runtime error occurred here
Exit code: 1.
The guard is inverted
// src/interpreter/mod.rs:7385-7392
let max_iterations = if end_num > 1000000.0 {
u64::MAX // Effectively no limit for large end values, rely on timeout instead
} else {
// Allow up to 10001 iterations to accommodate loops that need exactly 10000
10001
};
Measured boundary, one run per row (count from 1 to N, body increments a counter):
| N |
exit |
result |
| 10000 |
0 |
total: 10000 |
| 10001 |
1 |
Count loop exceeded maximum iterations (10001) |
| 10002 |
1 |
same |
| 999999 |
1 |
same |
| 1000000 |
1 |
same |
| 1000001 |
0 |
total: 1000001 |
So the runtime refuses a 10,001-iteration loop and then happily runs a
1,000,001-iteration one.
Because the guard reads end_num and not the trip count, a downward loop is
capped by the value it counts down to:
count from 2000000 down to 1: // end_num = 1, so max_iterations = 10001
→ error[ERROR]: Count loop exceeded maximum iterations (10001), exit 1 — a
2,000,000-iteration loop rejected because its end value is small.
The cap is also redundant and inconsistent
- The comment's fallback already works on its own:
count from 1 to 100000000000
is terminated by the execution timeout after 60 s, exit 1
(timeout_seconds default 60, src/config.rs:159). Runaway protection does not
depend on the 10001 cap.
- No other loop form is capped. Verified at 20,000 iterations and beyond:
repeat while → completes (also verified at 630,000); for each over a
20,000-element list → completes.
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 identically on the repo build G:\repos\wfl\target\release\wfl.exe
(version 26.8.2) — not a regression, long-standing.
No .wflcfg in scope for the repro (the repo's own .wflcfg was not in the
repro's directory chain).
Context
Found while porting G:/repos/JShrink/src/JShrink/Minifier.php (a 738-line PHP
JavaScript minifier) to WFL. The minifier walks a 630,176-character input, so its
main loop needs hundreds of thousands of iterations.
This was encountered, not blocking: the port uses repeat while throughout,
which is uncapped, so nothing was stubbed. It is filed because the failure hits
ordinary, correct programs — a beginner writing count from 1 to 20000 gets a
runtime abort for a construct the docs present without qualification, and the
error text names a limit that appears nowhere in the documentation.
Suggested direction (maintainer's call): if a guard is wanted at all, key it on
the actual trip count rather than on end_num, apply it uniformly to repeat
and for each, make the limit configurable in .wflcfg, and document it. If the
timeout is considered sufficient — which the 60 s result above suggests — removing
the cap is the simpler fix.
Summary
countloops abort at runtime after 10,001 iterations — but only when the end value is ≤ 1,000,000. A loop with a larger end value runs uncapped, socount from 1 to 20000fails whilecount from 1 to 1000001succeeds. The guard is keyed on the end value rather than on the iteration count, and it protects the small loops while abandoning the large ones.Reproduction
Command:
Expected
total: 20000, exit 0.Docs/03-language-basics/loops-and-iteration.md:5-29documents the construct ascount from <start> to <end>with no stated bound ("Thecountloop repeats aspecific number of times"), and
:274usescount from 1 to 100as a plainexample. No documentation anywhere mentions an iteration cap —
grep -rniE "maximum iterations|iteration limit|10001|10000 iterations" Docs/returns zero hits.
Actual
Exit code: 1.
The guard is inverted
Measured boundary, one run per row (
count from 1 to N, body increments a counter):total: 10000Count loop exceeded maximum iterations (10001)total: 1000001So the runtime refuses a 10,001-iteration loop and then happily runs a
1,000,001-iteration one.
Because the guard reads
end_numand not the trip count, a downward loop iscapped by the value it counts down to:
→
error[ERROR]: Count loop exceeded maximum iterations (10001), exit 1 — a2,000,000-iteration loop rejected because its end value is small.
The cap is also redundant and inconsistent
count from 1 to 100000000000is terminated by the execution timeout after 60 s, exit 1
(
timeout_secondsdefault 60,src/config.rs:159). Runaway protection does notdepend on the 10001 cap.
repeat while→ completes (also verified at 630,000);for eachover a20,000-element list → completes.
Environment
WebFirst Language (WFL) version 26.8.4C:\Program Files\wfl\bin\wfl.exeAlso reproduces identically on the repo build
G:\repos\wfl\target\release\wfl.exe(version 26.8.2) — not a regression, long-standing.
No
.wflcfgin scope for the repro (the repo's own.wflcfgwas not in therepro's directory chain).
Context
Found while porting
G:/repos/JShrink/src/JShrink/Minifier.php(a 738-line PHPJavaScript minifier) to WFL. The minifier walks a 630,176-character input, so its
main loop needs hundreds of thousands of iterations.
This was encountered, not blocking: the port uses
repeat whilethroughout,which is uncapped, so nothing was stubbed. It is filed because the failure hits
ordinary, correct programs — a beginner writing
count from 1 to 20000gets aruntime abort for a construct the docs present without qualification, and the
error text names a limit that appears nowhere in the documentation.
Suggested direction (maintainer's call): if a guard is wanted at all, key it on
the actual trip count rather than on
end_num, apply it uniformly torepeatand
for each, make the limit configurable in.wflcfg, and document it. If thetimeout is considered sufficient — which the 60 s result above suggests — removing
the cap is the simpler fix.