The plan for adding to every documented procedure, statement and operator the run-time errors it raises, and for the tools that measure them. Prompted by a reader on Discord: no VB or VBA reference answers which errors can this raise, and a reader writing an error handler needs exactly that.
Like WIP.md, this file is not rendered through tbdocs.
Batches 0, 1 and 2 are done, reviewed by the owner, and committed on staging. The
tooling, the central page and every page of the VBA Strings module are in place: 339 cases
in errors/VBA.Strings.yml, and a Common Errors section on 28 of the module's 30 pages.
Batch 3, VBA Conversion, is done and committed on staging (not pushed; 2026-10-21):
299 cases in errors/VBA.Conversion.yml, all agreeing with the data in twinBASIC 32-bit and
VB6 on BETA 1010, and a section on 23 of the module's 28 pages (CVar, Fix, Int and Nz raise
nothing of their own; the index documents nothing). No row is bug: pending, and no question
is open: the owner settled the last one (the width of a negative Double's Hex, an
extension) on 2026-10-21.
Every difference from VB6 found so far has a reproducer. Filed upstream, under bugs/filed/
(2026-10-22): redim-inverted-bounds-error, string-null-character, and from batch 3
val-suffix-overflow-no-error, error-function-above-65535,
and cdate-string-decimal-out-of-range. (hex-double-above-2-to-62 was withdrawn on
2026-10-22, owner: 64-bit VBA does the same; its reproducer is deleted.)
Still queued in BUGS-TO-REPORT.md, under bugs/: database-compare-no-error,
instr-invalid-lcid-empty-string and strconv-east-asian-lcid. Batch 3 also cites two
entries queued before it, date-out-of-range-no-error and clnglng-2-to-63-no-overflow,
whose HTML comments now name the Conversion pages too.
Batch 4, VBA Math, is done and committed on staging (not pushed; 2026-10-22): 132
cases in errors/VBA.Math.yml, all agreeing with the data in twinBASIC 32-bit, twinBASIC
64-bit and VB6 on BETA 1010, and a section on 15 of the module's 17 pages (ByteSwap raises
nothing; the index documents nothing). The owner ruled each difference a bug, and the three
are filed: abs-minimum-widens-type (twinbasic#2541), trig-above-2-to-63 (Cos, Sin and Tan
in one report, #2539) and round-null-negative-digits (#2540). No row is bug: pending and no question is open.
Batch 5, VBA DateTime, is done and committed on staging (not pushed; 2026-10-22):
328 cases in errors/VBA.DateTime.yml, all agreeing with the data in twinBASIC 32-bit and
VB6 on BETA 1010, and a section on 17 of the module's 20 pages (Now and Timer raise nothing;
the index documents nothing). Seven differences are queued in BUGS-TO-REPORT.md, under bugs/:
date-time-set-errors, dateadd-year-below-100, dateadd-date-out-of-range-overflow,
datepart-null-checks-interval, datepart-firstdayofweek-out-of-range,
datepart-firstweekofyear-out-of-range and
datepart-week-year-100 (a hang, so a WARNING by hand and not in the data). The owner
answered batch 5's questions on 2026-10-22 (Batch 5 findings). No row is bug: pending
and no question is open.
Four runtimes: 64-bit twinBASIC answers to 64-bit VBA (owner, 2026-10-22, after fafalone
pointed it out). errors.mjs run and messages now measure twinBASIC 32-bit (tb), twinBASIC
64-bit (win64), VB6 (vb6) and 64-bit VBA (vba64, run in 64-bit Access: scripts/lib/vba.mjs)
by default, for every file. Batches 2 to 5 and Core.yml were re-checked on BETA 1010: all
1,109 cases agree with the data in all four runtimes (1,005 of them run in 64-bit VBA), and
messages agrees in all four. See The 64-bit VBA re-check below.
The 64-bit VBA results are in the queued entries of BUGS-TO-REPORT.md (2026-10-22): nothing of the re-check is left.
Batch 6a is done (2026-10-22), VBA Information and the non-interactive procedures of
Interaction (the owner split batch 6, 2026-10-22; 6b, the interactive ones, comes after).
302 cases, all agreeing in all four runtimes; sections on 27 pages; the owner's two answers
applied; four new entries queued in BUGS-TO-REPORT.md, and DISP_E_NOTACOLLECTION added to
latebound-hresult-mapping (Batch 6a findings). No row is bug: pending.
Batch 6b is done (2026-10-27), Interaction's interactive procedures, under three new
harnesses (dialog, keys, window): 164 cases, all agreeing in all four runtimes (the whole
VBA.Interaction.yml, 355 cases, too); sections on AppActivate, InputBox, MsgBox, SendKeys and
Shell; DoEvents quiet; Beep nothing (D3). argument-errors-unspecified-error extended, three
entries queued (Batch 6b findings). No row is bug: pending, and no question is open.
Batch 7 is done (2026-10-27), VBA FileSystem, under a new harness (files): 448 cases in
errors/VBA.FileSystem.yml, all agreeing in all four runtimes; sections on 21 of the module's 22
pages (Reset raises nothing; the index documents nothing). The owner's rulings applied (six
extensions, three bugs); file-errors-unspecified extended by 30 cases, three entries queued
(Batch 7 findings). No row is bug: pending, and no question is open.
Batch 8 is done (2026-10-28), VBA Collection and ErrObject: 259 cases in
errors/VBA.Collection.yml and errors/VBA.ErrObject.yml, all agreeing in all four runtimes;
sections on 10 of the 20 member pages (Count, both Clears, Items, Keys, LastDllError and
LastHresult raise nothing; HelpFile, HelpContext and ReturnHResult cannot be probed, their
assignment failing twinBASIC's build; the indexes document nothing). The owner's rulings
applied (two extensions, one bug); three entries queued (Batch 8 findings). No row is bug: pending, and no question is open.
Batch 9 is done (2026-10-28), VBA Financial: 349 cases in errors/VBA.Financial.yml,
all agreeing in all four runtimes; sections on all 13 member pages (the index documents
nothing); -2147418113 (E_UNEXPECTED) added to messages.yml and measured. The owner's
rulings applied: five entries queued, argument-errors-unspecified-error extended, and PV's 11
for an infinite result an extension (Batch 9 findings). No row is bug: pending, and no
question is open.
Batch 10 is done (2026-10-28), VBA HiddenModule,
Compilation and TbExpressionService: 280 cases in errors/VBA.HiddenModule.yml,
VBA.Compilation.yml and VBA.TbExpressionService.yml, every one only: tb (VB6 and VBA have
none of these procedures), all agreeing in both twinBASIC builds; sections on 35 pages; two new
harnesses, native and expression; four HRESULTs added to messages.yml and measured. The
owner's rulings applied: four entries queued (nothing-argument-crashes,
expression-service-dangling-tokens, expression-service-error-numbers,
vbacastobj-no-type-mismatch), and CreateGUID's braceless form and AllocMem(0)'s error are
the spec (Batch 10 findings). No question is open.
Next: batch 11, Core file I/O statements.
- Data-driven (2026-10-20). Each procedure's errors are data: the cases that raise them. The page section, the probe that measures them and the check that keeps them true are generated from that data. Hand-written tables cannot be re-verified on a new BETA.
- Measured, not copied. Every row is observed in twinBASIC and in VB6. Microsoft's pages
are a source of cases to try, never of answers. A statement about VB6 names only what was
run in VB6 (a
Longarray, not "any typed array"). - Runtimes (replaced 2026-10-22). Four, for everything: twinBASIC 32-bit, compared with
VB6, and twinBASIC 64-bit, compared with 64-bit VBA, whose behaviour must be respected
(owner): a difference between twinBASIC 64-bit and 64-bit VBA is a bug, exactly as one from
VB6 is, with a NOTE and a queued reproducer. (Before: twinBASIC 64-bit for operators and
Mathonly, and no VBA.) Where VB6 and 64-bit VBA differ, twinBASIC does as 64-bit VBA does, in both builds (owner, 2026-10-28): matching 64-bit VBA is an extension against VB6 (only: [tb, vba64]cases, a NOTE in the prose), and matching neither is a bug whose reproducer asserts 64-bit VBA's number. - Scope.
docs/Reference/Default/(VBA, VB, VBRUN),docs/Reference/Core/(statements and operators) anddocs/Reference/Built-In/. NotContributed/. - Existing error prose moves into the tables. A page that states an error in a sentence ("an LCID that is not a valid locale identifier raises error 5", "if compare is Null, an error occurs") loses the sentence when its table is written; what the sentence explains beyond the number goes into the row's When.
- A difference from VB6 is a bug (the owner's rule for bugs generally): the table states
what twinBASIC does, a NOTE on the page states what VB6 does, and the difference is queued in
BUGS-TO-REPORT.md. Where VB6 has no counterpart (LongLong, twinBASIC
additions), there is nothing to compare and nothing to file on that ground. Ask the owner
when twinBASIC accepts what VB6 refuses: it may be an extension (2026-10-21:
JoinandFilterof aLongorDoublearray are, and so areHexandOctof any number in theLongLongrange, with a negative non-integer type in theLongLongform; a NOTE in the page's prose, twinBASIC-only quiet cases, novb6:row). The owner also ruled, for batch 3, thatValignoring a suffix's range,Error$above 65535 andCDateout of range are bugs. - An error that only the test setup raises is not listed (2026-10-22): 70 when the probe lacks the privilege to set the clock documents the setup, not twinBASIC. Nor is a NOTE written for it. An extension is decided the same way for every argument (2026-10-22): it may be one where its behaviour makes sense and has no bugs; otherwise it is a bug.
- Content that twinBASIC and VB6 both contradict is the page's fault (2026-10-21): it is
commented out, not deleted, as
<!-- VBA: <why it is out> ... -->, outside any table, so the VBA text is kept for docs that will one day switch between VB6, VBA and twinBASIC (WIP.Authoring.md, twinBASIC deviations from VBA to flag). The first were thevbUseCompareOptionrows of six compare tables and two "Null is returned" sentences of String.
A ## Common Errors section (owner, 2026-10-21, on a reader's request) after the page's main
text and before ## Example:
| Error | When |
|---|---|
5 (&H5), Invalid procedure call or argument |
start is less than 1, or length is negative |
94 (&H5E), Invalid use of Null |
Mid$ is given a string that is Null |
- The number is given in decimal and in VB hexadecimal, as a 32-bit Long (owner,
2026-10-20): -2147467259 (
&H80004005), 5 (&H5), in the table and in the VB6 NOTE alike. - The number links to its row on the central page as
tb:Errors#<n>(owner, 2026-10-21): the central page hasshortcut: Errorsin its front matter and its anchors are the bare numbers. How shortcuts work: WIP.Authoring.md, Cross-section linking. - The message is
Err.Descriptionof the raised error (owner, 2026-10-20), in italics. - When is a condition on the arguments or the state, in the reader's terms --- not the probe's expression. Name the form when only some forms raise (Mid$, LCase$).
- What is listed: what the procedure raises from its own arguments and state, and any
error a Null argument raises, in the procedure or in converting the argument (owner,
2026-10-21: which parameters convert is invisible to the reader;
Filter(Null, "a")'s 13 is listed). What is not: errors anything can raise (7 Out of memory, 14 Out of string space, 28 Out of stack space), errors in converting an argument to the type its page gives for the parameter (13 for a value of the wrong kind, 6 for a number outside that type's range, such as 2147483648 for a Long), and compile-time diagnostics (TB codes). The central page says this once. A 6 for a value inside the documented type's range is listed (ChrB(256): the page says Long). - The heading takes the level of the page's Example heading:
###on the VBA and Core pages, which the build promotes to##;##on a page without an Example or See Also heading. - Rows of a page that raise the same number are one table row, their When cells joined with
<br>in the order of the data. - A row where twinBASIC raises nothing and VB6 raises (
error: 0, below) is a NOTE only. - A procedure that raises nothing gets no section --- unless its absence is surprising enough
to state (
none:). - The section is written between markers, so regenerating it replaces only it.
docs/Reference/Run-time-Errors.md, permalink
/Reference/Run-time-Errors, shortcut: Errors, under Reference Section in the nav and in
the book's The Core Language part after Compiler Constants. Hand-written: what the sections
list and leave out, how a number is written, the message rule, and a NOTE on Err.Raise of a
number without a message. Generated by errors.mjs write, between <!-- errors-index: -->
and <!-- /errors-index -->:
- a table, one row per number in
messages.yml: the number, anchored by itself (#5,#-2147467259), its hexadecimal, its message (andError$'s where it differs), and Listed by, a link (22 pages) to the number's line below, or---; - a section Pages that list each error: a line per number some table names (twinBASIC's
number, not the
vb6:one), anchored#listed-<n>, its pages comma-separated and sorted by title (owner, 2026-10-21).
VB's numbers come first in order, then the HRESULTs in the order of their hexadecimal.
check_errors.mjs checks the index as it checks the sections, and that the page keeps its
shortcut.
What errors.mjs messages found (BETA 1010):
- Every number from 1 to 65535 with a message of its own: 88, and twinBASIC 32-bit and VB6
agree on every one, for
Error$(n)and forErr.DescriptionafterErr.Raise n. They are all inmessages.yml, whether or not a table names them yet. - HRESULTs are in
messages.ymlonly when the data names one: -2147467259 so far. For these the two messages differ, and the file says{ raised, error }. The sections showErr.Description(owner: settled); showingError$(n)instead would be a one-line change inrenderSection(.raisedto.error). - Differences from VB6 found here, and where each went (batch 8 took them up):
Err.Raise nwithout a description, Application-defined or object-defined error in VB6 and empty or a system message in twinBASIC, is filed (err-raise-defaults, #2504; the central page's NOTE). VB'sErr.Raiseof a common HRESULT as its own number is queued (err-raise-hresult-mapping).Err.Raise 65536, 5 in VB6, is an extension (owner, 2026-10-28; the Raise page's NOTE).Error$above 65535 is filed (error-function-above-65535). Left alone: twinBASIC'sError$of a few HRESULTs is VB's message (Error$(&H80070057)is Invalid procedure call or argument), VB6's is generic.
Their reference is on the Tools pages:
errors.mjs and
check_errors.mjs.
errors/--- the data: one YAML file per area (VBA.Strings.yml,Core.yml, ...) andmessages.yml, each error number's message.errors/harnesses/<name>.bas--- the harnesses (Harnesses, below).node scripts/errors.mjs run [<file.yml>] [--only <regex>]--- builds one probe from the data per runtime, runs it, and compares each case with the data. All four runtimes by default (--runtime tb|win64|vb6|vba64, repeatable, picks some). By hand only: it needs twinBASIC, VB6 and 64-bit Office. A run of every file in all four takes a few minutes; one page about 15 s.--onlyis a plain regex (a leading/is read as a delimiter: writeMid\.md$, not/Mid\.md). A case a compiler refuses is reported and dropped, and the probe rebuilt.generate <dir>writes the probes without running them;--keepkeeps the onesrunbuilt.node scripts/errors.mjs write--- writes each page's section and the central page's index.node scripts/errors.mjs messages--- measuresmessages.ymlin twinBASIC 32-bit and VB6.scripts/check_errors.mjs--- the gate intest.batand CI: data in order, every section whatwritemakes of it, no section without data, the index current, the central page's shortcut. No runtime needed. A data problem makesrunexit 2 with only "the data has problems";check_errors.mjsnames it.scripts/lib/errors-data.mjs--- what the two share (renderSection,renderIndex,errorAnchor,CENTRAL_SHORTCUT,RUNTIMES,expected,runsIn, ...).scripts/lib/vba.mjs---VbaSession, 64-bit VBA. Its opening comment says why each part is as it is; in short: VBA has no host but Office (VBE7.DLL cannot be hosted without the vendor-only SDK); Access, not Excel, because Excel needs "Trust access to the VBA project object model", a security setting that is the user's; a PowerShell driver (as-EncodedCommand) started bylaunchOnDesktop, so Access's windows are on a private desktop; one Access for the whole run (owner: the overhead), taking jobs fromjob.txt, each a new database in its own folder, the module imported through the VBE,Application.Run "Main", the module removed again (else closing the database asks to save it) and the database closed. A compile error is a VBE box that Run waits on; answering it does not free Run (the VBE stays in break mode), so any box ends that Access by its pid and the next job starts another (about 2 s). VBA compiles a Sub when it is first called, so the refused case is the first that did not report; the vba dialect's Main reportsstartfirst, so an error in the declarations is told apart.findAccess64reads Access.Application's LocalServer32 and checks the exe is x64.
How a probe runs a case: a Private Sub C<n>() of its own, Dim Result As Variant, the
case's decl lines, On Error Resume Next, its setup lines (an error there is reported as
a setup failure, not as the case's result), then Result = <expr> or its run lines, then
case <n> <Err.Number> <Err.Description>. twinBASIC: a tree for tbrun --exe, module
Probe with Sub Main and a [RunAfterBuild] that only prints, so the IDE run passes and
the exe runs the cases. VB6: Probe.vbp and Module1.bas through runRepro, appending to
out.txt line by line, so a case that kills the exe still leaves the ones before it. A case
that runs under a harness calls <name>_Begin before its setup lines and <name>_End
after its run lines (and before a setup failure is reported); the harness's code is in the
module once, before Report.
# errors/VBA.Strings.yml
beta: 1010 # the BETA the data was last measured on; titles the VB6 NOTE
pages:
Reference/Default/VBA/Strings/Mid.md: # the page, as a path under docs/
rows: # one table row each, in this order
- error: 5 # the number twinBASIC raises
when: "*start* is less than 1, or *length* is negative" # the When cell
cases: # every case must raise `error` in twinBASIC
- Mid("abc", 0) # a string is an expression: Result = Mid("abc", 0)
- Mid("abc", 1, -1)
- error: 94
when: "**Mid$** or **MidB$** is given a *string* that is **Null**"
cases:
- Mid$(Null, 1)
- MidB$(Null, 1)
quiet: # cases that must raise nothing; never shown on the page
- Mid(Null, 1)
- Mid("abc", 4)
Reference/Default/VBA/Strings/InStr.md:
rows:
- error: 0 # twinBASIC raises nothing, VB6 raises: a NOTE, no table row
when: "**InStr** is given a *compare* of 2 (**vbDatabaseCompare**)"
vb6: 5 # what VB6 raises instead (0: nothing); writes the NOTE
bug: database-compare-no-error # a bugs/<slug> reproducer, or pending until queued
cases:
- InStr(1, "abc", "a", 2)
Reference/Core/ReDim.md:
rows:
- error: -2147467259
when: an upper bound is less than its lower bound
vb6: 9
bug: redim-inverted-bounds-error
cases:
- { decl: Dim X() As Long, run: ReDim X(0 To -1) }
- { decl: Dim X() As Long, run: "ReDim X(5 To 4, 1)" }
quiet:
- { decl: Dim X() As Long, setup: ReDim X(3), run: ReDim Preserve X(5) }
Reference/Default/VBA/DateTime/Date.md:
harness: clock # every case of the page runs under errors/harnesses/clock.bas
quiet:
- { run: "Date = #1/1/1601#\nclock_Expect #1/1/1601#, Null" } # and checks the value
- { run: "Date = 36526\nclock_Expect #1/1/2000#, Null", only: tb }
Reference/Default/VBA/Strings/Join.md:
quiet:
- decl: Dim A(1) As Long # block form for a case whose text holds commas
expr: Join(A)
only: tb # not run in VB6 (twinBASIC syntax, or an extension)
- { expr: 'StrConv("a", 256)', only: tb } # single quotes around double quotesA case is an expression, or a mapping with expr or run (statements; a multi-line string
for several) and optionally decl (Dim lines), setup (statements before it),
only: and harness: <name> (also on a row or a page; a case takes its own, else its
row's, else its page's). only: names the runtimes a case runs in: tb (both twinBASIC
builds only: twinBASIC syntax, an extension, a twinBASIC addition), or a list holding tb and
one of vb6, vba64: [tb, vba64] for LongLong code (64-bit VBA has it, VB6 not),
[tb, vb6] for a case Access cannot answer for (vbDatabaseCompare, below).
What each runtime is expected to raise for a row (expected in errors-data.mjs): tb its
error:; win64 its win64: if given, else error:; vb6 its vb6: if given, else
error:; vba64 its vba64: if given, else vb6:, else win64:, else error:. So a row
that differs from VB6 expects 64-bit VBA to agree with VB6, and every other row expects it to
agree with twinBASIC 64-bit; vba64: is written only where that is not so (refused when it
equals the default). A row where 64-bit VBA's number differs from twinBASIC 64-bit's needs a
bug:, as a vb6: row does. A row of error: 0 needs a vb6: or vba64: other than 0 and
a bug:. The NOTE names both runtimes when they agree (When ..., VB6 and 64-bit VBA
raise error N, ...; twinBASIC raises none. / ..., instead.), else one sentence each, the
VBA one compared with twinBASIC 64-bit; a row none of whose cases runs in 64-bit VBA says
nothing of it. A row's message: is twinBASIC's own Err.Description for its number,
where it is not the number's message in messages.yml (Array's destructuring form raises 5
as expected an array): the table shows it, and run checks it in twinBASIC. run does
not check an HRESULT's description in VB6 or VBA, which describe every HRESULT as Automation
error with Windows' text on a second line (expectedDescription). A page with no rows gets no section; one with none: "<sentence>" and
no rows gets a section holding that sentence. messages.yml maps each number to its message,
or to { raised: <Err.Description>, error: <Error$(n)> } where they differ; run fails a
case whose Err.Description is not the raised text. A number a row names must be in the
file; a new HRESULT is added by hand and then measured with errors.mjs messages. A data page
must have title: and permalink: in its front matter.
A harness is code a case runs under, for a procedure whose effect must not happen in a probe
(setting the system clock) or must be observed (the value it would set). It is one file,
errors/harnesses/<name>.bas (a lowercase name), the same source for twinBASIC, VB6 and
VBA (owner, 2026-10-22): module-level code that defines Private Sub <name>_Begin() and
<name>_End(), and whatever helpers its cases call (<name>_...). VB6 compiles only the
branch of an #If that is true, so #If VBA7 holds what VB6 cannot parse (PtrSafe,
LongPtr; VBA7 is true in twinBASIC and in VBA 7, and 64-bit VBA refuses a Declare
without PtrSafe, so never #If TWINBASIC for this), and #If Win64 what differs in a 64-bit build; a pointer is kept in a
Variant between calls, and in a typed local for an As Any parameter (a Variant passed
ByVal to one passes the Variant). A harness must refuse what the real thing refuses: one
that accepts more hides errors (the clock harness first passed Time$ = "24:00:00", which
Windows refuses). check_errors.mjs reports a harness the data names and has no file for.
clock---Date,Date$,Time,Time$.clock_BeginpatchesSetLocalTimeandSetSystemTime(kernelbase and kernel32) with a jump to a function that records the SYSTEMTIME and succeeds where Windows would (it asksSystemTimeToFileTime, and fails with error 87 otherwise), andAdjustTokenPrivileges, which VB6 calls first and twinBASIC never does, with one that succeeds;clock_Endputs the bytes back. The patch is of the probe's own exe, for one case at a time, and the real clock is never set.clock_Expect Day, Clockraises 1001 when the date part or the time part set is not the one given (Nullskips it); it does nothing when the case has already raised. 32-bit and 64-bit; in 64-bit VBA it patches Access's own process for the case, and works (every Date and Time case agrees).registry(batch 6a) ---SaveSetting,GetSetting,GetAllSettings,DeleteSetting. A case names the applicationregistry_App,tbdocsErrorsProbeand the process id (the four runtimes run at once), andregistry_Beginandregistry_Enddelete that application's whole key underHKCU\Software\VB and VBA Program SettingswithSHDeleteKeyW(DeleteSettingraises 5 in VB6 and VBA for a key that has a subkey), keeping the case's error (raised again with its description once the key is gone), so nothing a case writes stays in the user's registry. A case'ssetupmay save what it reads. Check after a run that notbdocsErrorsProbe*key is left.dialog(batch 6b) ---MsgBox,InputBox. AWH_CBThook on the probe's thread notes each top-level#32770it creates and, as it is activated, hides it and sendsWM_COMMANDfrom Cancel, else the first push button that is not Help (a posted one never reaches the box in Access;EndDialogskips the dialog procedure, and twinBASIC's InputBox then faults in OLEAUT32). A guard patchesMessageBoxIndirectA/W(twinBASIC calls the W form, VB6 and VBA the A form) and refuses a style with any of &H260000 (another desktop: service notification, its NT 3.x form, default desktop only), whichdialog_Endreports as 1002; the six other MessageBox entry points are refused outright.dialog_Expect Countraises 1001;dialog_Count,dialog_Style,dialog_Buttonrecord the last box. VB6 builds a probe withdialogcases without Unattended Execution (ATTENDED_HARNESSES), andVbaSessionends Access only at a box seen at two polls.keys(batch 6b) ---SendKeys. Patcheskeybd_event(twinBASIC's way) andSendInput(VB6's and VBA's) with fakes that record each event and succeed, andSetWindowsHookExA/Wwith one that refuses a journal-playback hook as Windows does without UIAccess (VB6 and VBA then useSendInput).keys_Expect "10d 41d 41u 10u"raises 1001 for other events (<vk hex><d|u>, orU<char hex><d|u>for a Unicode one).window(batch 6b) ---AppActivate. ASTATICtop-level window titledwindow_Title(with the process id), shown without activation;window_Pid,window_Hide,window_Minimize,window_Visible,window_Minimized. On the private desktop no window becomes the foreground one, and AppActivate raises nothing for that; in twinBASIC, activating a new window there takes 2 s from the third on (not on the user's desktop), soerrors.mjsgives a twinBASIC probe 120 s.files(batch 7) --- every procedure that names or opens a file.files_Beginmakesfiles_Dir,tbdocsErrorsProbeand the process id under the temporary folder, makes it the current directory (Windows' own calls, so no statement under test sets it up) and fills it:a.txt(abc, CR LF,def, CR LF: 10 bytes),empty.txt,ro.txt(read-only),hid.txt(hidden), the empty foldersubandfull, which holdsf.txt. A case names its files relatively.files_Endcloses every file, puts the current directory back and deletes the folder whatever its files' attributes, keeping the case's error; it deletes nothing whose path does not end in the folder's own name.files_Beginraises 1003 when it cannot make or enter the folder, so a case never runs in another folder.files_Attr(name)isGetFileAttributesW(-1: no such file),files_Expect Condraises 1001,files_NoDriveis a letter no drive has. A probe line that startsReset:is a label, not the statement: putReseton a line of its own (run: "Reset\n..."). Check after a run that notbdocsErrorsProbe*folder is left in%TEMP%.native(batch 10) --- twinBASIC only: every case that uses it isonly: tb, so it uses twinBASIC syntax. What the (Default) module's procedures take:native_Icon(the sharedIDI_APPLICATIONicon),native_Bitmap(a 2 x 2 bitmap thatnative_Enddeletes unlessnative_Keepsays a picture owns it),native_Window(a hiddenSTATICwindow, made bynative_Begin),native_Handler(aGetMessageHookHelper.GetMessageHandlerthat does nothing),native_StdIid(Data1)(the address of the interface ID{Data1-0000-0000-C000-000000000046}, forvbaCastObj) and the enumerationnative_Kind(-3 to 7).expression(batch 10) --- twinBASIC only. In a built exe the first error the expression service (Eval,TbExpressionService) raises in a process has the description Automation error, every later one Unspecified error;expression_Beginraises the first once and clears it, so a case's description does not depend on which case runs first (--only).
YAML pitfalls, each met in batch 2:
- A flow list breaks on a comma inside a case:
cases: [Mid(Null, 1)]is two items. Use a block list (- Mid(Null, 1)) for any case with a comma; a flow list only for cases with none. - Quote a case that holds
:, starts with a quote, or sits in a{ }mapping and holds a comma; in a{ }mapping, a case holding double quotes goes in single quotes.
Read first: WIP.Harness.md (tbbuild, tbrun), WIP.Build.md
(gates), WIP.Authoring.md (page template, Cross-section linking,
twinBASIC deviations from VBA to flag), and scripts/lib/vb6.mjs's opening comment
(driving VB6).
- List the batch's pages, and for each read its syntax, parameters, settings tables and every sentence that mentions an error, Null or a limit.
- Explore first. Write a temporary
errors/Explore.ymlthat names the real pages and puts every candidate case underquiet:, thennode scripts/errors.mjs run Explore.yml: each case's line shows what every runtime raised,[tb 5, win64 5, vb6 5, vba64 5]. MarkLongLongcasesonly: [tb, vba64], notonly: tb. A case VBA refuses costs an Access restart and a rebuild, andrungives up after 25 rebuilds: keep twinBASIC-only cases (Bin,CType(Of ...),ValDec,Shift*) markedonly: tbwhile exploring. Try, for every parameter: Null; 0, -1, -2; the upper limit and one past it (strings stop at 1,073,741,823; 2147483647); a value just past a Long (2147483648#, which raises 6 and is not listed); an empty string; a 2-D array, an unallocated one and a typed one for an array parameter; every value a settings table on the page lists, and values between and beyond them (a compare above 1 is an LCID: 1033 is valid, 99999 is not). Probe a boundary from both sides before writing it. DeleteExplore.ymlbefore committing. - Write the data in the area's file, one row per condition, quiet cases for each
boundary's passing side, then
node scripts/errors.mjs run <file> --only "<Page>\.md"until it says all agree with the data. node scripts/errors.mjs write, then edit the pages: remove each error sentence the table now carries; comment out, as<!-- VBA: -->, what both runtimes contradict; move a VB6 claim that the table's NOTE now makes. Check index pages (a module'sindex.md) for the same claims.- A difference from VB6: a row with
vb6:(orerror: 0),bug: pending, and either queue it yourself (node scripts/bug_repro.mjs new <slug>, the entry in BUGS-TO-REPORT.md, then the slug in the data) or list it for the owner. Where twinBASIC accepts what VB6 refuses, ask the owner whether it is an extension before queuing. - Gates:
build.bat(0 broken),node scripts/check_errors.mjs,test.bat;check.batonce per batch. Commit on staging (the owner's choice for this work), one-line message, no Co-Authored-By trailer. When another session shares the checkout,git addby path, never-A, so its files are not swept into the commit. - Update Where things stand and the batch's row below, and list the questions for the owner.
What the probes must respect (BETA 1010; errors.mjs already does all of it):
- Run cases in the built exe (
tbrun --exe), and print withTbRun.Out, whichtbrunadds to the project:Debug.Printwrites nothing in an exe. A[RunAfterBuild]run in the IDE reads every module-level variable as 0 once one initializer reads another module's variable (bugmodule-init-reads-other-module-ide), and may differ in other ways. - A
tbrun --exeprobe brings its ownSub Main, without the console template'stbxMain.twin; twoMains make the build fail with 'Main' is ambiguous. - Never an unhandled error in a probe, and
MsgBoxandInputBoxonly under thedialogharness,SendKeysonly underkeys: otherwise each opens a modal box on a private desktop, or sends keys. Every case runs underOn Error Resume Next. - Keep the generated code plain. A module-level or class-field initializer cannot call a
procedure of the project, and a class field that reads another field fails the build (bug
class-field-init-reads-field). - VB6 cannot compile twinBASIC syntax (
LongLong,Returnin a function, inlineDim x =, generics), and has no counterpart for twinBASIC additions such as StrConv'svbUTF8(256). A twinBASIC-only case saysonly: tb. - One case can stop a whole project: a compile error anywhere fails all of it.
runbuilds a failing project again without the case named in the error. - A case that allocates near the string limit raises 14 (
String(1073741823, "a")in 32-bit): use it to show that the 5 above the limit is a check of its own, never as a row. - A case that crashes stops the probe, and every later case reports
none(the probe stopped before it ended: exception 0xc0000005), as a hang does. Explore one page per probe (a data file holding only that page), then one case per probe for a page that stops; a case that crashes is a WARNING by hand, never a case. - The probe is one module: a case cannot declare a class, an interface or a
Type. A harness can hold aType, anEnum, aDeclareand procedures; a fact that needs a class (GetNameFromDispIDon a twinBASIC class) is checked withtbrunon a tree of its own and stated in the page's prose.
Both runtimes agree unless said. The pages and data carry each of these; they are here so a later batch knows what to look for.
vbUseCompareOption(-1) raises 5 in InStr, InStrRev, Filter, Replace, Split and StrComp, though VBA-Docs lists it: their rows are<!-- VBA: -->comments, and the module's index no longer offers it. Omitting compare still follows Option Compare where the page says so.- A compare above 1 is an LCID: 3, 4, 1024, 1033 pass; 255, 65536, 99999 raise 5.
InStrBignores compare. vbDatabaseCompare(2): InStr, Filter and StrComp take it as an LCID where VB6 raises 5 (database-compare-no-error); Replace and Split take it in both.- The LCID check and empty strings: twinBASIC's InStr checks it when a string is
zero-length, VB6's does not (
instr-invalid-lcid-empty-string); InStrRev, Replace and Split check it in both; StrComp checks it in neither when a string is Null or zero-length. - Limits: Mid, Left, Right (not the B forms), String and Space raise 5 above 1,073,741,823 characters; InStr raises 5 for a start above 1,073,741,824, InStrRev and InStrB do not; FormatCurrency raises 5 for numDigitsAfterDecimal above 255, FormatNumber and FormatPercent take 2147483647. Chr raises 5 outside 0--255 (on a code page that is not double-byte), ChrW outside -32,768--65,535, ChrB 6 outside 0--255.
- Settings ranges that raise 5: MonthName's month outside 1--12; WeekdayName's weekday outside 1--7 and firstdayofweek outside 0--7; Format's firstDayOfWeek outside 0--7 and firstWeekOfYear outside 0--3; FormatDateTime's namedFormat outside 0--4; the Format... functions' tristates other than -2, -1, 0 and numDigitsAfterDecimal below -1; Split's limit and Replace's count below -1; StrConv's unlisted values and any sum that combines vbUnicode, vbFromUnicode, vbUTF8 or vbFromUTF8 with another value.
- Null: every $ form raises 94 for a Null string where the Variant form returns
Null;
String(3, Null)raises 94 in twinBASIC only (string-null-character); the Format... functions return for a Null expression. - StrConv's East Asian conversions (vbWide, vbNarrow, vbKatakana, vbHiragana) raise 5 on a
system whose locale is not East Asian; given an East Asian LCID VB6 converts and
twinBASIC still raises (
strconv-east-asian-lcid). - Extensions (owner):
JoinandFiltertake aLongorDoublearray; VB6 raises 5 and 13 for aLongone.
Both runtimes agree unless said.
- What a C function lists: 6 for a value outside its type (after rounding:
CByte(255.5),CInt(32767.5); -0.5 rounds to 0 and passes), 13 for a string that is not a number, 94 for Null. Their parameter is any expression, so the conversion is their own work, and listed; 13 for an argument of the wrong kind on a page that names a numeric parameter (Fix("abc"),Str("abc"),CVErr("abc")) is not. An Error value converts quietly inCBool,CByte,CInt,CLng,CDblandCStr, and raises 13 inFix,Int,Hex,Str,ValandCDate. An object argument (91 for Nothing) was left out: it is the default member's error. - Constants: a probe of a
Doubleboundary near 2^62 or 2^63 uses a variable (X = 2 ^ 63), never a literal: the constant 2^63 converts wrongly (clnglng-2-to-63-no-overflow), and a 32-bit build can raise a late 6 after a constant overflow (const-double-expression-after-overflow-32bit). CLngPtr: its 32-bit row haswin64: 0(run --runtime win64 --only CLngPtrchecks it).CDatetakes any number other than a Decimal with no error (date-out-of-range-no-error, VB6 6), a string holding such a number with none (VB6 13), and raises 6 for a Decimal (VB6 13; bothcdate-string-decimal-out-of-range). The page has a WARNING.CVDateraises 13 for all of these in both, and returns Null for Null.Val: 13 for a fraction with%or&; VB6 raises 6 for a whole number outside the suffix's type (%,&,@), twinBASIC returns it (val-suffix-overflow-no-error, owner: a bug).ValDecignores the suffixes.Error/Error$: VB6 raises 6 above 65,535 (after rounding), twinBASIC returns a message (error-function-above-65535, owner: a bug; taken from batch 8's list). Negative numbers pass.CVErrraises 5 outside 0--65,535 (after rounding).- Extension (owner):
HexandOcttake any number in theLongLongrange, where VB6 raises 6 outside aLong. ASingleorDoubleof 2^62 or more in magnitude raises 6 inHex,OctandBin(-2^62 itself passes); aLongLong,Decimalor string does not. 64-bit VBA does both (the re-check, below), so neither is an extension nor a bug against it. - Extension (owner, 2026-10-21): a negative
Single,Double,Currency,Decimalor numeric string givesHex16 characters (Oct22,Bin64) in twinBASIC, theLongLongform; VB6 gives 8 (11), theLongform:Hex(-1#)is"FFFFFFFF"in VB6. A negativeIntegerorLongagrees. The Hex and Oct pages state it in their extension NOTE, and the tables give each type's width. Nothing to queue; not an error, so not in the data.
Both runtimes agree unless said; win64 agrees with win32 on every case.
- Null raises 94 in Atn, Cos, Sin, Tan, Exp, Log, Sqr, Sgn, Rnd and Randomize, and as Round's numdecimalplaces or the Shift functions' shiftamount. Abs and Round return Null for a Null first argument.
- Abs keeps its argument's type, so the smallest
Currencyraises 6 in both. For the smallest typedIntegerorLongVB6 raises 6 and twinBASIC returns aLongor aDouble(abs-minimum-widens-type; the smallestLongLonggives aDoubletoo). AVariantholding either widens in both. - Trigonometry past 2^63: VB6 raises 5 for Cos, Sin and Tan from 9.223372E+18 in magnitude
(found by bisection). twinBASIC returns a value up to 2^63; from 2^63 Cos and Sin return
the argument itself, and Tan raises -2147348207 (
&H80021111), Automation error, whoseError$is generic (trig-above-2-to-63; WARNINGs on Cos and Sin). The number is inmessages.yml. - Limits: Exp raises 6 above 709.782712893384; Log raises 5 for 0 or less (Empty and
-0 included); Sqr 5 for a negative number (-0 passes). Rnd's 6 above the
Singlerange is the conversion of its parameter, not listed. - Round: 5 when numdecimalplaces rounds below 0 (-0.4 passes), and above 22 only for a
Single,Double,Dateor string expression;Integer,Long,Currency,Decimal,Booleanand Empty take anyLong. VB6 raises 5 forRound(Null, -1), twinBASIC returns Null (round-null-negative-digits);Round(Null, 23)is Null in both.
Both runtimes agree unless said.
- A Calendar case must set
vbCalGregagain. In VB6Calendar = vbCalHijrisucceeds and stays, so every later case of the probe runs under the Hijri calendar and the VB6 column drifts. The data's cases end withCalendar = vbCalGreg. In twinBASIC every assignment raises -2147467259 and the compiler warns TB0009, [Calendar] has not yet been implemented. The owner chose not to report it (2026-10-22): its cases areonly: tb, with no VB6 NOTE, and a NOTE by hand on Calendar and on the module's index says it is not implemented. - Setting the clock: Date and Time run under the
clockharness (Harnesses), so an accepted value raises nothing andclock_Expectchecks what would be set; without it a probe has no privilege and every accepted value raises 70. A 70 is never a row (owner, 2026-10-22: it documents the test setup, not twinBASIC). Neither runtime checks a date or a time it parses: both pass month 13 or February 30 to Windows, and Windows refuses them, and any date before January 1, 1601. Where Windows refuses, twinBASIC raises -2147467259 and VB6 5.Date = NullandTime = Nullraise 94 (VB6 13);Date$takes only mm-dd-yyyy andTime$only hh:mm:ss (VB6 also takes m-d-yyyy, m/d/yyyy, mm-dd-yy, hh:mm, hh); all indate-time-set-errors. Both read the string "0.25" given toTimeas 0:25. Date =andTime =of a number are an extension (owner, 2026-10-22). twinBASIC reads the number, or a string holding one, asCDatedoes: whole days from December 30, 1899 (EmptyandFalseare that day,Truethe day before, being -1),Byte,Long,Single,CurrencyandDecimalalike, and 13 outside the range of dates.Datesets the date part and keeps the time of day;Timesets the time part of theCDatevalue (0.25 and 1.25 are 6:00, -0.25 and -1.25 too; 0.999999 rounds to 0:00 of the same day). VB6 sets exactly the same for a Date value (Time = CDate(1.25)is 6:00), and raises 13 for a number. Each is a twinBASIC-only quiet case that checks the value, and a NOTE on Date and on Time says it. Checked in 32-bit and 64-bit.- The 13 rule: a function whose parameter is "any expression that can represent a date"
(Day, Month, Year, Weekday, Hour, Minute, Second, DatePart, DateValue, TimeValue) raises 13
for a string that is not a date, a number outside -657434 to 2958465, and an Error
value; Null returns Null in the part functions and raises 94 in DateValue and
TimeValue (TimeValue's page said Null is returned: now a
<!-- VBA: -->comment). DateValue refuses any number with a whole part (DateValue(36526)is 13), and a Date passes. - DateAdd and DateDiff raise 6 for a date number outside the range (VB6 13,
dateadd-date-out-of-range-overflow). Listed, though the page gives the parameter as Variant (Date): it differs from VB6 and from the part functions. - DateAdd: 5 for an interval that is not a setting (case-insensitive, no spaces) and for a
result after 9999 or before year 100; with "yyyy", "q" or "m" a result in years 0 to 99
becomes 1930--2029 silently (
dateadd-year-below-100, VB6 5; a WARNING). - firstdayofweek outside 0 to 7 is a bug (owner: an extension only if it makes sense
and has no bugs). Over every date of 1999 to 2001: a negative value acts modulo 7, but
DatePart("w")takes 8 as 1 and 9 up as nothing at all (255 gives a weekday of -2),DatePart("ww")takes 8 and 9 as 1,DateDiff("ww")8 to 13 as 1 (datepart-firstdayofweek-out-of-range; WARNINGs on DatePart and DateDiff). VB6 checks it only where the interval uses it. - firstweekofyear outside 0 to 3 gives, for every value tried (-15 to 2147483647), the
same results as 0 and 1, which agree on this system (its own setting is January 1). That
could be an extension, but whether twinBASIC takes it as 0 (the system's setting) or 1
cannot be told apart here; the owner ruled it a bug (
datepart-firstweekofyear-out-of-range). - DatePart:
DatePart("x", Null)raises 5, VB6 returns Null (datepart-null-checks-interval).DatePart("ww", ...)of January 1 or 2, 100 never returns withvbFirstFullWeekand returns -98980 withvbFirstFourDays(datepart-week-year-100; a WARNING). A case that hangs stops the probe, and every later case reportsnone: run a suspect alone before reading the cases after it. - DateSerial: 5 when the date is outside January 1, 100 to December 31, 9999, after its
two-digit-year reading (
DateSerial(-1900, 1, 1)passes, -1901 raises); 6 above an Integer is the conversion, not listed. TimeSerial cannot reach a date outside the range with Integer arguments: its page's sentence saying it can is a<!-- VBA: -->comment. pageLevelnow takes the level of the page's last Example or See Also heading, so Date and Time (an Example a level down under each property) get###like their See Also. Their sections were placed before See Also by hand, as markers;writefills them.
Owner's answers (2026-10-22): 1, Date = and Time = of a number are an extension; 2,
the week arguments may be extensions where they make sense and have no bugs; 3, Calendar is
not reported; 4, no 70 rows. Then: a firstweekofyear outside 0 to 3 is a bug.
Status (2026-10-22): data, sections and page edits done and committed:
errors/VBA.Information.yml and errors/VBA.Interaction.yml, 302 cases, all agreeing with the
data in all four runtimes; sections on 27 pages. Left: the owner's answers to the questions
below (bug grouping; large numbers in Partition and Choose), then each bug: pending row's
reproducer and entry in BUGS-TO-REPORT.md, and the RaiseEventByName crash's entry (the
WARNINGs on both pages are written; put the slug in an HTML comment beside each). Page edits
made: LBound/UBound's IMPORTANT no longer names error 9; the error sentences of DeleteSetting,
SaveSetting, CreateObject, GetObject and Switch went into the tables; Environ's "a zero-length
string is returned" for a missing position is a <!-- VBA: --> comment (both VB runtimes raise
5); Array's RhsArray no longer says it raises; QBColor's and RGB's parameters are stated as
Integer. twinBASIC 64-bit agrees with 32-bit on every case. The registry harness names the
application with the process id, since run probes the four runtimes at once and they shared
one key (a VB6 case failed when another runtime's cleanup removed its section).
- twinBASIC raises -2147467259 (
&H80004005, Unspecified error) where VB6 and 64-bit VBA raise 5, 9 or 13 for an argument the procedure refuses: LBound/UBound's dimension out of range (VB 9), Switch with an odd number of arguments (5), Partition's start below 0, stop not above start, interval below 1 (after rounding) (5) and a string number (13), Environ's number of 0 or less (after rounding),"", Empty, True, and a position with no entry (5: the page's "zero-length string is returned" is wrong for both VB runtimes), GetObject() with no arguments (13), CallByName's calltype not 1 to 4 (VB 438) and an object of Nothing (5), and the four registry procedures'""appname, section or key, and DeleteSetting of a missing application, section or key (5). The same family asdate-time-set-errorsandredim-inverted-bounds-error. Proposal for the owner: one bug entry for the family. Above the VBIntegerrange VB raises 6 where twinBASIC raises -2147467259 (Environ's number from 32,768, LBound's dimension 32,768). - COM creation raises the raw HRESULT where VB maps it: CreateObject of an unknown ProgID,
a CLSID without
new:, a number: -2147221020 (MK_E_SYNTAX);new:with an unregistered CLSID: -2147221164 (REGDB_E_CLASSNOTREG);new:with a malformed one: -2147221005 (CO_E_CLASSSTRING);"": 5. VB6 and 64-bit VBA raise 429 for each. GetObject: a missing file -2147221014 (MK_E_CANTOPENFILE), with a class -2147467262 (E_NOINTERFACE) where VB raises 432; an unknown class -2147221005 and no running instance -2147221021 (MK_E_UNAVAILABLE) where VB raises 429. A remote server that does not resolve: -2147023174 in all four; a"notaprogid:xyz"moniker: -2147221020 in all four. Proposal: a second bug entry. - CallByName raises DISP_E_ HRESULTs where VB maps them: a wrong argument count
-2147352562 (
DISP_E_BADPARAMCOUNT; VB 450), a property given arguments it does not take -2147352559 (VB 451), and an error the COM member raises -2147352567 (DISP_E_EXCEPTION; VB raises the member's own number: Dictionary.Add of a key that exists 457, Remove of a missing key 32811, CompareMode set after an Add 5).""as procname: 438, VB 450. The page already has a NOTE for a twinBASIC member's constant Err.Raise; this widens it. Proposal: a third entry, or fold into the page's existing NOTE's bug. - A VBA Collection is not reachable through CallByName in VB6 or 64-bit VBA: even
CallByName(C, "Count", vbGet)raises 438 there (twinBASIC: 0). The data usesScripting.Dictionary. Not queued: the VB runtimes' own limitation. RaiseEventByNameandRaiseEventByName2crash (access violation, 32-bit and 64-bit) given an object that is not an event source (Collection,Dictionary); Nothing raises 91. A crash cannot be a case: a WARNING by hand on both pages, and a bug to queue.- 32-bit twinBASIC follows 64-bit VBA, not VB6, for large numbers: Partition of a number
or stop beyond a Long (VB6 6, twinBASIC and 64-bit VBA none); Choose of an index beyond
the Single range (VB6 none, twinBASIC and 64-bit VBA 6). Owner, 2026-10-22: a documented
extension ("tB can extend VB6"): the cases are
only: [tb, vba64], novb6:and no bug, and each page has a NOTE in its prose saying what VB6 does. - Owner, 2026-10-22: the bugs are grouped as proposed: one entry for the -2147467259 family,
one for CreateObject's and GetObject's raw HRESULTs, one for CallByName's
DISP_E_HRESULTs, and one for the RaiseEventByName crash. Progress (reproducers underbugs/, each atestsreproducer with avb6/side, run andrepro.jsonwritten; entries in BUGS-TO-REPORT.md not written yet unless ticked):- reproducer
argument-errors-unspecified-error(3 PASS controls, 17 FAIL), [ ] entry - reproducer
createobject-getobject-raw-hresults(3 PASS, 8 FAIL), [ ] entry - reproducer
callbyname-dispatch-hresults(3 PASS, 5 FAIL), [ ] entry. Narrowed:latebound-hresult-mapping(queued) already hasDISP_E_BADPARAMCOUNT(450), so the wrong-count rows cite it; this entry is what is left: a COM object's own error reachesCallByName(any call type) and a late-bound propertyLetstatement as -2147352567, though a late-bound method statement gets it right (D.Add "x", 2raises 457 in all four); and""as procname raises 438, VB 450. -
&H80020011(DISP_E_NOTACOLLECTION, VB 451, for a statement too:D.Count(1)) is inlatebound-hresult-mapping's table and its reproducer (two cases, VB6 sideSetCode 25); the CallByName rows for argument counts (450) and arguments to a property (451) cite it - reproducer
raiseeventbyname-no-events-crash(5 PASS, 5 FAIL, 32-bit and 64-bit; classesEventSourceandListener), [ ] entry. Found with it: an event that no handler is attached to raises -2147467259, whereRaiseEventraises nothing; with a handler both functions work. A NOTE on both pages, and the entry's second point - the data's
bug: pendingreplaced by the slugs; the WARNINGs' HTML comments - the four entries' text in BUGS-TO-REPORT.md (
argument-errors-unspecified-error,createobject-getobject-raw-hresults,callbyname-dispatch-hresults,raiseeventbyname-no-events-crash), each with its module(s) verbatim; all five reproducers packed (latebound-hresult-mappingagain). Batch 6a is done; next, 6b.
- reproducer
- Agree in all four: LBound/UBound of an unsized or erased array 9, of a Variant holding no array 13, dimension Null 94; QBColor outside 0 to 15 after rounding 5; RGB's negative component (after rounding) 5, above 255 taken as 255; Null 94 in QBColor, RGB, Choose's index, Environ, CreateObject, GetObject, CallByName, and every registry argument; Switch, IIf and If of a string that is not a Boolean 13; the Is* functions, TypeName, VarType, ObjPtr, StrPtr, IMEStatus, Erl and Command raise nothing.
- twinBASIC-only: RGBA's negative component 5; RGB_R/G/B and RGBA_A raise only 94 for Null; TranslateColor raises 5 for a high byte other than 0, 1, 2 or &H80, and for a system colour index above 30 (&H8000001E passes, &H8000001F raises); Array's destructuring form raises 5 for a right side that is not an array (Null too) and 9 for one with fewer elements or unsized; CallByDispId: an unknown DISPID 438, calltype outside 1 to 4 and Nothing -2147467259, a wrong argument count -2147352562.
- Type of a parameter the page leaves out: QBColor's and RGB's parameters are Integer in VB (32,768 raises 6 in all four): state the type on the page, and the 6 is then the conversion, not listed.
The pages: AppActivate, Beep, DoEvents, InputBox, MsgBox, SendKeys and Shell. Each of them acts outside the probe --- a box, a key, a window's focus, a process, a sound --- so the question for each is what the probe may let happen, and what it must intercept.
The problem with the rule as it stands. The batch row allows MsgBox and InputBox never,
and SendKeys only with strings refused before a key is sent. That leaves MsgBox and InputBox
with no quiet cases at all, so no row could be written either: a case such as
MsgBox("x", 7) cannot be tried without the risk that it succeeds and opens a box. And which
SendKeys strings are refused before a key is sent is just what the probe has to find out. The
design therefore makes each effect safe in the probe's own process, as the clock harness
does for the system clock, so that every case can be tried.
Each is one errors/harnesses/<name>.bas for all four runtimes, #If VBA7 for the
declarations and the callback's signature, a pointer kept in a Variant (Harnesses).
dialog(MsgBox, InputBox) --- two parts.- The answer.
dialog_Begininstalls aWH_CBThook on the probe's own thread (SetWindowsHookEx(5, AddressOf dialog_Hook, 0, GetCurrentThreadId())), anddialog_Endremoves it. The hook notes each window of class#32770it sees created (HCBT_CREATEWND), and when one of those is activated (HCBT_ACTIVATE) it hides it and postsWM_COMMANDfor one of its buttons: Cancel (2) when the box has one, else its first button that is not Help (9). It records each box it answered: its title, its text (the static control 65535 in a message box) and the button pressed, so thatdialog_Expect Countcan raise 1001 when a quiet case did not show the box it should (a call that silently does nothing is told apart from one that showed and returned). Windows the hook did not see created --- Access's own --- are left alone. The real MessageBox and the real dialog run: what they refuse is what the probe measures. - The guard. A box can be shown on a desktop other than the probe's:
MB_SERVICE_NOTIFICATION(&H200000), its NT 3.x form (&H40000) andMB_DEFAULT_DESKTOP_ONLY(&H20000) put it on the input desktop, the user's, where no thread hook sees it, and a buttons of -1 or 2147483647 has those bits. Sodialog_Beginalso patches every MessageBox entry point of user32 (MessageBoxA/W,MessageBoxExA/W,MessageBoxIndirectA/W,MessageBoxTimeoutA/W) with a jump to a guard, asclock_Patchdoes: a style with any of the bits &H260000 is refused (the guard returns 0, setsERROR_INVALID_PARAMETERand counts a guarded call, whichdialog_Endreports as error 1002 so that the case can never pass silently); any other style is passed to the real function (bytes put back, the call made, the patch put again; the probe has one thread). VB6's MSVBVM60 importsMessageBoxIndirectAandMessageBoxA; what twinBASIC and VBE7 call is the first thing step 1 of the plan finds out, and no case with those bits runs in a runtime until a guard is shown to catch that runtime's calls.
- The answer.
keys(SendKeys) --- a jump patch, as the clock's, of the calls that inject input:SendInputandkeybd_eventin user32, andSetWindowsHookExA/Wfor a journal-playback hook (idHook1), which MSVBVM60 imports and may be how VB6 sends keys. The fakes record each key event (virtual key, up or down) and report success; nothing is injected. A journal-playback hook is recorded and its callback driven by the harness until it reports the end (HC_GETNEXT,HC_SKIP), as Windows would; whether this part is needed at all is step 1's answer for VB6.keys_Expect "<keys>"raises 1001 when the recorded events are not the ones given ("a{ENTER}"written as the harness's own short form, the plan's step 4). So a quiet case checks what a string sends, and a row can say whether keys before a bad token are sent first ("a{BAD}"). The private desktop is not the input desktop, so a key that slipped past the patch could not reach the user's desktop either; the patch is what makes the result independent of that.window(AppActivate) ---window_Begincreates a visible top-level window of the probe's own thread titledwindow_Title(tbdocsErrorsProbeand the process id, asregistry_App), andwindow_Enddestroys it, so that a case has something to activate by title, by a prefix of it, and by task id (window_Pid, the probe's own process id). A private desktop is never the input desktop, so Windows may refuse to bring a window to the foreground there; if step 1 shows that it does, the harness also patches the activation call each runtime makes (SetForegroundWindow, MSVBVM60 imports it) with a fake that refuses what the real one refuses (IsWindowfalse) and records the window, so that no error of the private desktop reaches the data (the owner's rule: an error only the test setup raises is not listed).
Shell needs no harness: the batch row's limit stays (a program that does not exist,
cmd.exe /c exit, and files that are not programs, such as win.ini of the Windows folder).
A process it starts inherits the probe's private desktop and its kill-on-close job. No case
names a .bat, .cmd or anything else CreateProcess would run.
DoEvents gets quiet cases and no section. Beep has no arguments and nothing to raise: no case (it would sound in the owner's room four times a run) and no section.
- VB6 is built without Unattended Execution for a page under the
dialogharness. WithUnattended=-1, VB6's MsgBox writes to the event log and shows nothing, so the probe would measure the log.runReprotakes an option for it, anderrors.mjspasses it when a case of the probe usesdialog. A box the exe opens is still closed by the launcher; under the hook there should be none. VbaSessionends Access at a box only when it has stayed for two polls. Today any visible#32770of Access ends it (Tooling), and the hook's box exists for an instant before it hides it. A compile error's box stays until answered, so nothing it catches is lost.tbrunneeds nothing: a box the launcher sees and closes is reported indialogs, whicherrors.mjsreads only when the probe stopped.check_errors.mjsneeds nothing new: the three harnesses are files it already checks for.- WIP.Errors.md: What the probes must respect says MsgBox and InputBox only under the
dialogharness, and the batch row is rewritten.
- D1. May a probe call MsgBox and InputBox under the
dialogharness (hook and guard)? Without it the two pages get at most their Null rows, and only where a Null is refused before a box opens, which cannot be known without trying. - D2. May SendKeys be given strings that send keys, under the
keysharness, once step 1 shows the patch catches every runtime's calls (the first such run with the owner's OK, since it is the one that would send a key if the patch missed)? - D3. Beep: no case and no section, as above, or a guard (
MessageBeepandBeeppatched) and quiet cases? - D4. If VB6 (or 64-bit VBA) raises an error for SendKeys or AppActivate on the user's own desktop because of Windows itself (VB6's SendKeys is known to raise 70 Permission denied on Windows Vista and later when its journal hook is refused), is that a row, a NOTE, or nothing? It is the runtime's behaviour on a current Windows, not the test setup's, so the 2026-10-22 rule does not settle it. The harness hides it, so step 1 checks it once on the user's desktop only with the owner's OK, or the owner answers from what is known.
Owner's answers (2026-10-22): D1 yes (MsgBox and InputBox under dialog). D2 yes
(SendKeys strings that send keys, under keys, once step 1 shows the patch catches every
runtime's calls). D4 nothing: no row and no NOTE, since the documentation does not
document VB6, and such an error is a VB6 bug for practical purposes; it is not checked on
the user's desktop either. D3 left for now: Beep takes no arguments and either works or
does not, so it gets no case, no harness and no section.
What each runtime calls (a temporary trace harness that patched each candidate export with a
refusing fake): MsgBox MessageBoxIndirectW (twinBASIC) and MessageBoxIndirectA (VB6, VBA);
InputBox DialogBoxIndirectParamW (twinBASIC), DialogBoxParamA template &HFBF (VB6, VBA);
SendKeys keybd_event (twinBASIC), and in VB6 and VBA a journal-playback hook, refused on
Windows 10, then SendInput one event at a time; AppActivate SetForegroundWindow and SetFocus
in all; Shell CreateProcessW with STARTF_USESHOWWINDOW and windowstyle as wShowWindow in
all. The harnesses (Harnesses) cover exactly these.
Owner's decisions (2026-10-27):
- Bugs, queued: SendKeys' -2147352567 and AppActivate's, Shell's, MsgBox's and InputBox's
-2147467259 join
argument-errors-unspecified-error.interaction-missing-argument-checks: Shell windowstyle 10 (SW_SHOWDEFAULT, the one valueSTARTUPINFOmay not hold; VB takes 0 to 9 after rounding and nothing done first changes it), InputBox xpos/ypos outside theIntegerrange (VB 6), context without helpfile (VB 5), helpfile Null raising 94 (VB 5), AppActivate of a hidden window (twinBASIC leaves it hidden; VB 5) and an unchecked wait, and MsgBox styles Windows refuses (button group 7 to 15: no box, 0 returned; should raise 5; VB shows an OK box).sendkeys-non-ascii: a character the layout has no key for is converted to ANSI and sent as a key (é as virtual key &HE9, ☺ as?, Ā as Shift+A); VB sends the character (KEYEVENTF_UNICODE).private-desktop-window-activation-delay: in an exe on a private desktop, activating a new window takes 2 s from the third (none on the user's desktop, run there once with the owner's agreement, none in VB6). - Extensions: AppActivate
""(64-bit VBA agrees; VB6 5); SendKeys wait"True"(VB 13); MsgBox passing every style Windows accepts (VB shows an OK box for a style it does not know: 6, two icons, &H400, -1). NOTEs on the pages. - Nothing documented: VB's
"((a))"(Shift left down), its doubled Shift for"+", and its keeping the keys of a refused string for the next SendKeys (arguably a VB bug; twinBASIC drops the string, which is right). D4: no error Windows makes VB raise on the user's desktop. - Unguarded MsgBox -1 and &H7FFFFFFF (owner's agreement): Windows refuses them for their button group before any box, so the service-notification bit never shows a box.
Pages: MsgBox and InputBox lose "If helpfile is supplied, context must also be supplied"
(the tables carry it); Shell's "or zero if the program could not be started" is a <!-- VBA: -->
comment (all four raise); SendKeys' {INS} is a <!-- VBA: --> comment (all four refuse it), its
PRINT SCREEN NOTE says why ({PRTSC} sends VK_PRINT, &H2A), and a count in braces is at most
8,175 in all four; AppActivate matches a title's end as well as its beginning.
Tooling found on the way: VB6 and VBA refuse a declaration after a procedure, so a probe
with two harnesses puts all their declarations first (splitHarness); Access never delivers a
posted WM_COMMAND to a box, so the dialog harness sends it; EndDialog from outside makes
twinBASIC's InputBox fault (a string it never set); a reproducer's VB6 project may call MsgBox and
InputBox when it carries the dialog harness (reproAnswersBoxes). The broker's startup race
(four clients, all ENOENT) is fixed on both sides (WIP.Broker.md).
twinBASIC 64-bit agrees with 32-bit on every case, and 64-bit VBA with VB6, so every difference is twinBASIC against both.
- -2147467259 where VB raises a number, almost everywhere (
file-errors-unspecified, queued since BETA 1005 and extended by 30 cases): 5, 52, 53, 54, 62, 63, 67, 68, 75, 76. The page's old hand NOTEs went; the rows' generated NOTEs replace them. The Core pages (Open, Name, Get, Put, Print, Line Input) keep their hand NOTEs until batch 11. What VB raises for a bad name differs by procedure: GetAttr 5 for<or a file name ending in a backslash, FileLen and FileDateTime 53 for those, Dir 52 for the backslash; a wildcard is 52 everywhere but ChDir, MkDir and RmDir (76) and Kill (53 when nothing matches). - Null raises 94 everywhere, but
CurDir(Null)andDir(Null)raise 13 in VB (filesystem-argument-checks). - Owner's rulings (2026-10-27). Extensions, with a NOTE saying what VB does and
twinBASIC-only quiet cases: Dir's attributes outside 0 to 63 (unknown bits ignored; VB 5);
a non-String argument to Dir and CurDir, converted as ChDir and ChDrive convert theirs in VB
(
Dir(5); VB 13); FileCopy of a source open for reading only, not locked against reading (VB 55 for any mode but Input); SetAttr's attributes wherever Windows accepts them ("tB extension where the attributes are valid for the underlying WINAPI; where the WINAPI fails, SetAttr must correctly raise an error"; VB 5 for anything but sums of 1, 2, 4, 32, and 16 on a folder); Width from 256 to 32,767 (VB 5). Bugs (filesystem-argument-checks): a negative Width (no error, a new line before each item), FileLen and FileDateTime of a wildcard that matches (the first match's value; VB 52), and with themInput(0)(-2147467259; VB returns"") and the Null 13. - Kill deletes hidden and system files (
kill-hidden-system-files, a WARNING): VB raises 53 for one named alone and skips it in a wildcard. - FileCopy's numbers (
filecopy-error-numbers): 70 for a folder or a read-only or hidden destination (VB 75), 55 for a copy onto itself or of a source openLock Read(VB 70). - SetAttr of a file open for writing: no error (VB 55),
setattr-open-file-no-error, queued since BETA 1005; its WARNING stays beside the generated NOTE. - Agree in all four: FileCopy's 53, 55 and 76; Binary-mode
Inputpast the end returns what is left, with no error;InputB(0)returns"";FreeFiletakes any rangenumber (nonzero after rounding is 256 to 511), and with 256 to 511 all in useFreeFile(1)returns 512, whichOpenaccepts; Reset closes files 256 to 511 too; Width does not wrap inside one item; Kill stops at an open file a wildcard matches, after the files before it. - Page edits: the error sentences of Dir, FileCopy, Kill, RmDir and Seek went into the
tables; Width's parameter is an Integer from 0 to 32,767 (32,768 is the conversion's 6);
CurDir's section moved below both functions, and ChDrive's, Dir's, MkDir's and RmDir's above
their See Also, which those pages put before the Example (
spliceSectionplaces a new section before the first Example, so a page with See Also first needs moving by hand once).
twinBASIC 64-bit agrees with 32-bit on every case, and 64-bit VBA with VB6.
- Assigning
Err.HelpFile,Err.HelpContext, orErr.ReturnHResultin a module procedure, compiles and fails the build with a codegen error (err-helpfile-helpcontext-codegen); in test mode the procedure aborts. So the data has no case for those three pages (a case would fail the whole probe): each page has a hand NOTE, and Raise's helpfile and helpcontext are covered on the Raise page. When it is fixed, addErr.HelpFile = NullandErr.HelpContext = Null(94 in VB6 and VBA) as rows. ReturnHResult's example now sits in a class (its build failed as a module function;examples.bat --buildis clean). - VB turns HRESULTs into VB numbers; twinBASIC does not (
err-raise-hresult-mapping):Err.Raiseof 22 HRESULTs tried raises a VB number in VB6 and 64-bit VBA (E_INVALIDARG5,E_NOTIMPL445, theDISP_E_ones,&H800A0000 + nas n, ...), andErr.Number =of one stores the VB number without raising. Data rows cannot hold onevb6:per HRESULT, so the Raise page has a hand NOTE with the table, and twinBASIC's side isonly: tbquiet cases (wrapped, below).Err.Raise &H800A0000raises nothing at all in VB6 and VBA; not written. - How a case says "raised what it was asked":
Err.Raise n(andErr.Number = n) thenIf Err.Number = n Then Err.Clear, so the case reports 0, and a runtime that raises or keeps another number reports that.Err.Number = 65536raises 5 in VB (anOn Error GoTohandler is entered);Err.Number = &H80070057only stores 5. - Collection (
collection-index-checks): aCurrency,DecimalorDateindex is refused (Item and Remove 9, Add's position 13; VB takes it as a number); Item of a number on an empty collection 9 (VB 5; Remove is 5 in both); Item and Remove ofNull, an object, an array, an error value or a number beyondLong9 (VB 13); Add's position of -1,TrueorEmpty13 (VB 9). Agree in all four: duplicate key 457 (""is a key); a non-String key 13 (a number too: VB does not convert it); both positions 5; a position on an empty collection 5,Nullthere too; a string position or index that is no key 5. - Owner's rulings (2026-10-28). Extensions, each with a NOTE in the page's prose and
only: tbquiet cases:Err.RaiseandErr.Number =of a number above 65535 (twinBASIC raises or sets it; VB raises 5), andCollection.Addwith before one more than Count (adds at the end) or after 0 (adds at the start; VB raises 9 for both). A bug, incollection-index-checks:Collection.Addwith a key of Empty adds the item without a key (VB 13). - Page edits: the error sentences of Add, Item, Remove and KeyCompareMode went into the
tables; the central page has a NOTE on
Err.Raiseof an HRESULT.
twinBASIC 64-bit agrees with 32-bit on every case. Unlike the earlier batches, VB6 and 64-bit
VBA differ where a result is infinite: VB6 returns infinity from SLN and PV, 64-bit VBA raises
6 or 11 (the rows carry vba64:). The values behind the defects below were read with a value
probe (twinBASIC through tbrun, VB6 through vb6run), not only the error numbers.
-
Every parameter is a Double in twinBASIC's declarations and in VBA's; FV, PV, Pmt and PPmt said Integer for nper (and PPmt for per), from VBA-Docs. Fixed on the pages.
-
The -2147467259 family (
argument-errors-unspecified-error): every argument check where VB raises 5 (and Rate's 6), in DDB, SLN, SYD, IPmt, PPmt, Pmt, NPer, Rate and IRR. -
Rate fails for almost every annuity: -2147467259 for every case tried with pmt other than 0, and for
Rate(10, 0, 1000, -2000); it answersRate(1, 0, 100, -110)andRate(2, 0, 100, -121). It also raises 5 for an nper below 1 with pmt 0 (VB answers), and never checks guess: Null,"abc"and an error value pass (VB 94, 13, 13). -
NPer raises 6 when rate and pmt have opposite signs, which is every loan and savings plan at a positive rate (
NPer(0.01, -100, 1000)is 10.59 in VB6); where it answers, the value is right. Where no number of periods exists it raises 6 or 11, VB 5. -
Null in an optional Variant argument raises 13, VB 94: pv/fv and type of FV, PV, Pmt, NPer, IPmt, PPmt and Rate, and DDB's factor.
-
Arrays: IRR, MIRR and NPV of an array never sized crash (access violation, 32-bit and 64-bit, IDE and exe; VB 5), a hand WARNING on each page and no case; a 2-D array raises -2147418113, Catastrophic failure (VB 5).
-
Missing checks that return a value (hand WARNINGs beside the generated NOTEs): IPmt and PPmt take any per (VB 5 when per is 0 or less, or nper + 1 or more); MIRR returns -1 for one element, a rate for a finance_rate or reinvest_rate of -1 (VB 5), and infinity or NaN for values with no negative element (VB 11); NPV returns NaN for a rate of -1 (VB 5).
-
Other differences: DDB refuses a life between 0 and 1 (VB answers); Pmt and PPmt raise 11 for an nper of 0 (VB 5); FV raises 5 for rate -1 and a negative nper (VB6 11, 64-bit VBA 6); PV raises -2147467259 for a rate of -1 (VB6 returns infinity or 0; 64-bit VBA 11, 6, or 0 for nper 0).
-
Infinite results: PV raises 11 (as 64-bit VBA does; VB6 returns infinity), SLN returns infinity (as VB6 does; 64-bit VBA raises 6). FV raises 6 in all four.
-
Agree in all four: Null in a required argument 94; a string that is not a number in an optional argument 13; Pmt's 6 and 11 for a rate of -1 or -2; IRR's guess 94 and 13; FV's 6; any type is taken (only 0 means the end of the period).
-
Page edits: SLN's, SYD's and DDB's "All arguments must be positive numbers" and NPV's (and MIRR's half of) "must contain at least one negative value and one positive value" are
<!-- VBA: -->comments (all four take what they forbid); IRR's and Rate's "If ... can't find a result after 20 tries, it fails" is reworded, the table carrying the error; the module index no longer says MIRR takes a guess or that every array needs both signs. -
Rate's wrong root (found writing its reproducer): for a lump sum (pmt 0) over an even number of periods it returns -2.1 where VB6 returns 0.1 (
Rate(2, 0, 100, -121),Rate(4, 0, 100, -146.41); one and three periods are right). Silent, so a WARNING by hand on Rate, inrate-fails. Not in the data: a value, not an error.
Owner's rulings (2026-10-28):
- The five entries accepted, as proposed, and queued with
testsreproducers and VB6 sides:rate-fails(3 PASS, 12 FAIL),nper-opposite-signs-overflow(3, 9),financial-null-optional-argument(3, 9),financial-array-arguments(3 PASS, 4 ACCESS_VIOLATION, 3 FAIL),financial-argument-checks(3, 16).argument-errors-unspecified-errorextended by one case per function (DDB, SLN, SYD, IPmt, PPmt, Pmt, NPer, Rate twice, IRR: 3 PASS, 35 FAIL now). - Where VB6 and 64-bit VBA differ, twinBASIC does as 64-bit VBA does. So PV's 11 for an
infinite result is an extension against VB6 (cases
only: [tb, vba64], a NOTE in PV's prose), and SLN's infinity is a bug (financial-argument-checks, asserting 6). - A rate of -1 in FV and PV, by the same rule: FV is to raise 6, and PV 0, 11 or 6 by
the sign of nper, as 64-bit VBA; in
financial-argument-checks, whose cases assert 64-bit VBA's numbers (its VB6 side prints VB6's, which differ in those four cases).
A reproducer's case asserts 64-bit VBA's number where the two VB runtimes differ; say so in the entry, since the VB6 project then disagrees with the module.
Batch 10 findings (VBA HiddenModule, Compilation, TbExpressionService, BETA 1010, 2026-10-28)
Every procedure is twinBASIC's own, so every case is only: tb and no row has a vb6:: a
defect here is one against the page or against what the procedure is for, not against VB.
twinBASIC 64-bit agrees with 32-bit on every case but AllocMem(2000000000) (a win64: 0
row). The pages with no section raise nothing of their own: Compilation's eight (compile-time
intrinsics), CreateGUID, the GetDeclared... six, StackArgsSize, StackOffset,
InterlockedIncrement32/Decrement32, Start, Stop, RuntimeCreateGetMessageHook,
UnprotectedAccess, vbaAryMove, vbaRefVarAry, AddStdLibraryBinder; and Emit, EmitAny,
InternalGetReturnAddress (fails a 64-bit build), AddCustomBinder and Bind have no case.
- Null raises 94 for every numeric or string argument, 424 for an object one
(
AddClassInstanceFlag,ConvertIconToBitmap,GetInheritedOwner,GetNameFromDispID,PictureToByteArray,vbaCastObj). - Raw HRESULTs where the procedure fails:
AllocMemof 0 or less -2147024882 (E_OUTOFMEMORY, as for a block too big);CreateStdPictureFromHandle-2147467259 for a handle of the wrong kind and -2147418113 for a Type outside 0 to 4 (a bitmap handle is never checked);ConvertIconToBitmap-2147467259 for anything but an icon, 5 and 13 for its BackColor asTranslateColor;PictureToByteArray-2147467262 for a non-picture;GetNameFromDispID's four (the page's hand table, now data), andShowPropertyPageEditor's -2147467261 and -2147467262. On an object of a twinBASIC classGetNameFromDispIDraises 5 (-4: -2147319765), checked withtbrunon a tree with a class: a sentence on the page. - The expression service (
Eval,Compile,Evaluate): every failure is -2147467259, a name it cannot resolve and a syntax error alike, and so is any error inside the expression (11, 13, 5, a bound object's 457): its number is lost (NOTEs on Eval and Evaluate).Eval("CVErr(5)")andEval("Len(Null)")raise it too, thoughLen(Null)is Null outside an expression. Some malformed expressions are accepted silently:"2 +","2 Mod","(2","2)"give 2 (WARNINGs on Eval and Compile; quiet cases). The first error of a process says Automation error, later ones Unspecified error; in the IDE, always Automation error (NOTEs; theexpressionharness). - Crashes (access violation, 32-bit and 64-bit; a WARNING by hand on each page, no case):
ConvertIconToBitmap(Nothing);GetInheritedOwnerof Nothing or of an object that is not a control (aCollectionin both builds, aScripting.Dictionaryin 64-bit);AddCustomBinder Nothing;AddCustomBinderObject name, Nothing; andV = RuntimeCreateGetMessageHook()into a Variant withoutSet(Set V =is fine, and a Let of another non-dispatch object, a twinBASIC class behind anIUnknowninterface, stores it with no error, so the fault is the hook object's). Also, with no WARNING (misuse of an internal or of a raw address):AddClassInstanceFlagof -1, an address of 0 toGetMem*,PutMem*andvbaCopyBytes(FreeMem 0andvbaObjAddref 0are quiet), andvbaCastObjgiven a number or (32-bit) Null as IID. - Pages corrected from what twinBASIC does:
CreateGUIDreturns 36 characters without braces (the page said{...}; the index pages too);vbaCastObjreads IID as the address of a GUID, so a String gives Nothing silently (the page said it took a String; its example andGetDeclaredTypeIid's now useVarPtrof a GUID and print the IID);PictureToByteArray(Nothing)is aBytearray with no bounds;FreeMem 0does nothing;Compilewithout a binder takes"1 + 1";AllocMem's andCompile's error sentences went into the tables. Added:RegisterMessagechecks no argument,InterlockedIncrement32/Decrement32wrap with no error. - Out of this batch:
Set X = objwhere X is declared as an interface obj does not have raises -2147467262 (E_NOINTERFACE); VB6 raises 13. Not checked in VB6 here, not queued: for a Core batch.
Owner's rulings (2026-10-28), and what was done:
- The crashes: one entry, GetInheritedOwner of a non-control in it: queued as
nothing-argument-crashes(3 PASS, 7 FAIL with ACCESS_VIOLATION; theScripting.Dictionarycase passes in 32-bit), the five WARNINGs tagged. - Malformed expressions: an entry,
expression-service-dangling-tokens(3 PASS, 8 FAIL), the two WARNINGs and the quiet cases' comments tagged. - The lost error number: "investigate what is most appropriate from a tB user's perspective,
and proceed accordingly". Both VB-family evaluators keep the error: VBScript's
Eval(MSScriptControl.ScriptControlfrom VB6) raises 11, 13 (Type mismatch: 'CInt'), 6, 5 and 450 for1 / 0,CInt("abc"),CInt(40000),Sqr(-1),Sqr(1, 2), and 1002 Syntax error or 1006 Expected ')' for every malformed expression; Access'sEval(64-bit VBA, VbaSession) raises 11, 13, 6, 5, returns Null forLen(Null), and raises 2431, 2434, 2435, 2436, 2439 for syntax errors and argument counts. So a bug: queued asexpression-service-error-numbers. Owner, then: "Eval should not be special casing errors" unless VB does, which it does not, and the entry asks only for errors to be passed through unchanged (2 PASS, 7 FAIL: 11, 13, 6, 5 and a bound object's 457, and the description Division by zero). Left out of it, and in no entry: a compile failure's number and description (no direct code to compare:Sqr(1, 2)does not compile),Eval("Len(Null)")andEval("CVErr(5)")(Eval fails where direct code returns a value), and the unstable description (the description NOTEs, untagged, and theexpressionharness). Owner: they stay NOTEs, with no entry (untagged, so no retired entry removes them: check them against each new BETA, as every labelled callout is). The Evaluate page's prose states the propagation, the tagged NOTEs say what BETA 1010 does. - "What does VBx do?" for
vbaCastObj: VB6's own__vbaCastObj(msvbvm60, called through aDeclare) returns the interface for a GUID's address, 0 for Nothing, and raises 13, Type mismatch for an interface the object lacks, a String's text read as an IID included; twinBASIC returns Nothing for both. VBE7.DLL exports no__vbaprocedure, so 64-bit VBA cannot be asked. Owner: a bug. Queued asvbacastobj-no-type-mismatch(3 PASS, 2 FAIL; VB6 side calling__vbaCastObj); the data's row iserror: 0,vb6: 13(its casesonly: tb, so the NOTE names VB6 alone); the page states the 13 and has a WARNING. - "What does VBx do?" for
CreateGUID: nothing to compare. VB6 and VBA have no CreateGUID, nor does Access (Application.CreateGUID438,Eval("CreateGUID()")2425), and no runtime exports one. VB code makes one withCreateObject("Scriptlet.TypeLib").Guid, which is braced ({...}, and two trailing NULs: 40 characters), orStringFromGUID2, braced. twinBASIC'sGetDeclaredTypeIidand its kin return the braced form;CreateGUIDthe 36-character form theClassIdandInterfaceIdattributes take. Owner: the existing behaviour is the spec, as the page now states it. - "What does VBx do?" for
AllocMem(0): nothing to compare: no AllocMem in VB6, VBA, msvbvm60 or VBE7 (their GetMem/PutMem family has no allocator). Windows' allocators (HeapAlloc,CoTaskMemAlloc,GlobalAlloc) return a block for 0 bytes. Owner: the existing behaviour is the spec: the page's row of -2147024882 for 0 or less stands.
Batches 2 to 5 and Core.yml in all four runtimes. Every runtime agreed with the data as it
stood, apart from the cases that follow, and twinBASIC 64-bit agreed with 32-bit everywhere.
- Every
vb6:row: 64-bit VBA agrees with VB6. So each existing bug holds against 64-bit VBA as well, andwritenow words those NOTEs VB6 and 64-bit VBA raise ... (17 pages).Absof the smallestInteger/Longraises 6 in 64-bit VBA too (abs-minimum-widens-type). vbDatabaseCompare(2): in Access, 64-bit VBA takes it (the database's sort order) and raises nothing forInStr,Filter,StrComp, as twinBASIC does; VB6 raises 5 (database-compare-no-error). That is Access's own meaning, which VBA documents as Access-only, so Access cannot answer for VBA in Excel or Word: the three cases areonly: [tb, vb6], and the NOTEs stay VB6-only. Whether VBA outside Access raises 5 is left unchecked (owner, 2026-10-22: not worth it; Excel would need the VBA object-model trust setting, the user's).only: tbcases 64-bit VBA can run:CLngLng(13 cases),CLngPtr(10),HexandOctbeyond aLong(15),Roundof aLongLong(1) are nowonly: [tb, vba64], and all agree:CLngPtrof 2^31 raises nothing in 64-bit VBA, as the rows'win64: 0say.Hex/Octin 64-bit VBA behave exactly as in twinBASIC: the wholeLongLongrange, and a negative non-integer gives 16 (22) characters:Hex(-1#)isFFFFFFFFFFFFFFFF,Hex(-1&)FFFFFFFF. So the batch-3 "extensions" are 64-bit VBA's own behaviour; the Hex and Oct pages' NOTEs now say so. And aDoubleorSingleof 2^62 or more raises 6 in 64-bit VBA too (Hex(2^62),Oct(2^62),-2^62passes). The filed bughex-double-above-2-to-62said otherwise: the owner withdrew it (2026-10-22, closed upstream), its reproducer is deleted, and the Hex, Oct and Bin NOTEs are now unlabelled statements of the limit (as in 64-bit VBA; Bin, which VBA lacks, as it does in Hex and Oct).CLngLng(2 ^ 63),CLngLng(9223372036854775808#)andCLngLng(9223372036854775807)raise 6 in 64-bit VBA; twinBASIC returns the smallestLongLong(clnglng-2-to-63-no-overflow, queued): the bug is confirmed against 64-bit VBA, and the CLngLng WARNING now says so. Add this to the entry's text in BUGS-TO-REPORT.md (not done yet).- Extensions stay extensions, and 64-bit VBA sides with VB6:
Date =/Time =a number (13),Joinof aLongarray (5),Filterof one (13),StrConv256/512 (5). Their hand NOTEs now say VB6 and 64-bit VBA raise ....Date = -657434staysonly: tb(it is a number).Bin,CType(Of ...),ValDec,Shift*andCalendardo not exist in VBA. messages: all 89 numbers agree in all four runtimes.
Done:
scripts/lib/vba.mjs(new),scripts/errors.mjs(four runtimes,generatewrites<dir>/vba, the VBA refusal reading),scripts/lib/errors-data.mjs(vba64:,only:lists,expected,runsIn,RUNTIMES, the vba dialect, the NOTE wording; the file keywin64:is gone),scripts/check_errors.mjs(probes for all of it),errors/harnesses/clock.bas(#If VBA7),errors/VBA.Math.yml(nowin64: true),errors/VBA.Conversion.ymlandVBA.Math.yml(only: [tb, vba64]),errors/VBA.Strings.yml(the three[tb, vb6]cases),docs/Documentation/Tools-Defects.md(the errors.mjs entry).errors.mjs writerun: 17 pages' NOTEs regenerated. Hand NOTEs edited: Hex, Oct, Join, Filter, Date, Time, CLngLng.
Then (2026-10-22, done): the 64-bit VBA results in the queued entries of
BUGS-TO-REPORT.md. In every one 64-bit VBA does what VB6 does, and twinBASIC 64-bit what
32-bit does. Each entry's Expected behavior now says as VB6 and 64-bit VBA ..., and its
Additional context that a 64-bit build gives the same results and which VBA was used
(VBA 7.1 (VBE7.DLL 7.01.1158) in 64-bit Microsoft 365 Access), the way a later entry should
say it too. The entries: clnglng-2-to-63-no-overflow (the four constants and four controls,
checked by a VbaSession probe), date-out-of-range-no-error (its six cases and three controls,
by a probe), date-time-set-errors, dateadd-date-out-of-range-overflow,
dateadd-year-below-100, datepart-firstdayofweek-out-of-range,
datepart-firstweekofyear-out-of-range, datepart-null-checks-interval,
instr-invalid-lcid-empty-string, strconv-east-asian-lcid (all from the data's run), and
datepart-week-year-100 (a probe: 52, 1, 52, 52; bug_repro.mjs run --arch win64 hangs as
32-bit does). Filed entries were not touched: their issues are the owner's to edit.
database-compare-no-error was left alone: Access's VBA takes 2 by design (above).
Each batch is one session. They are independent after batch 0, so any order works, and each can be run when there is budget. Status is kept here.
| # | Batch | Pages (about) | Status |
|---|---|---|---|
| 0 | Tooling: data format, probe generator (twinBASIC and VB6), checker, section writer, the gate | --- | done; reviewed 2026-10-20 |
| 1 | The central Run-time errors page | 1 | done; reviewed 2026-10-20; list of pages per number and tb:Errors shortcut 2026-10-21 |
| 2 | Pilot: VBA Strings --- settled the section's style with the owner |
30 | done: 28 sections, 339 cases; reviewed 2026-10-21 |
| 3 | VBA Conversion |
28 | done: 23 sections, 299 cases; owner's questions answered 2026-10-21 |
| 4 | VBA Math |
17 | done: 15 sections, 132 cases; 2026-10-22 |
| 2--5 | Re-check of batches 2 to 5 and Core in four runtimes (64-bit VBA added) | --- | done: all agree; committed 2026-10-22, see The 64-bit VBA re-check |
| 5 | VBA DateTime |
20 | done: 17 sections, 328 cases; owner's questions answered 2026-10-22 |
| 6a | VBA Information, and Interaction's non-interactive procedures: CallByName, CallByDispId, Choose, Command, CreateObject, GetObject, Environ, IIf, If, Partition, Switch, RaiseEventByName, RaiseEventByName2, SaveSetting, GetSetting, GetAllSettings, DeleteSetting (owner split batch 6, 2026-10-22) |
45 | done: 27 sections, 302 cases; four entries queued 2026-10-22 |
| 6b | Interaction's interactive procedures: AppActivate, Beep, DoEvents, InputBox, MsgBox, SendKeys, Shell, under the dialog, keys and window harnesses (Shell only with programs that do not exist or cmd.exe /c exit) |
7 | done: 5 sections, 164 cases; three entries queued 2026-10-27 |
| 7 | VBA FileSystem, under the files harness |
23 | done: 21 sections, 448 cases; three entries queued, one extended, 2026-10-27 |
| 8 | VBA Collection and ErrObject (and the Err.Raise differences under The central page) |
22 | done: 10 sections, 259 cases; three entries queued 2026-10-27; owner's rulings 2026-10-28 |
| 9 | VBA Financial |
14 | done: 13 sections, 349 cases; five entries queued, one extended; owner's rulings 2026-10-28 |
| 10 | VBA HiddenModule, then Compilation and TbExpressionService (Constants has nothing to raise) |
68 | 35 sections, 280 cases, harnesses native and expression; four entries queued; owner's rulings 2026-10-28 |
| 11 | Core file I/O statements (Open, Get, Put, Input, ...: errors 52--75) |
15 | not started |
| 12 | Core arrays and control flow (ReDim, Erase, Resume, Return, ...); ReDim has data already, in errors/Core.yml |
15 | not started |
| 13 | Operators: the matrix generator and its pages | 25 | not started |
| 14 | VB package (37 pages) and the VBRUN pages that document members rather than constants: properties and methods that raise 380, 383, 387 and the like; needs forms | 37 + part of 134 | not started |
| 15 | Built-In packages | 195 | not started |
Page counts are the folders' .md files on 2026-10-20, index pages included; a batch's first
step is to list the pages in it that document something that can raise.