You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

10 KiB

G-132 Draft the Tcl-on-windows errno catch-all report for manual submission

Status: proposed Scope: TEMP_REFERENCE/tcl-windows-errno-catchall-TICKET-DRAFT.md (submission-ready ticket text - STAGING ONLY, unversioned, and the agent does not submit it); this detail file (the tracked record: finding, verbatim reproducer, the submitted URL once the user files it, and the upstream disposition); src/bootsupport/AGENTS.md and src/make.tcl (the existing workaround and its warning - dated, not removed, if upstream fixes it) Goal: the Tcl-on-windows errno catch-all - every Win32 error above 267 surfacing as "invalid argument" - is written up as a submission-ready upstream report FOR THE DEVELOPER TO FILE MANUALLY, with a minimal reproducer and suggested fixes, and punkshell keeps the tracked record of the finding and whatever upstream decides, so the diagnosis is never re-derived the next time a high-numbered windows error appears as a nonsense message. Acceptance: the existing core.tcl-lang.org/tcl tracker is searched first and the result recorded here - an existing ticket is a valid outcome, and this goal then only links it and records its state; if unreported, a complete submission-ready ticket text is written to TEMP_REFERENCE/tcl-windows-errno-catchall-TICKET-DRAFT.md carrying title, summary, the minimal reproducer, the source citations (win/tclWinError.c - table extent 0..267 and the errorTable[1] fallback, verified present in both 8.6 and 9.x), the concrete windows code (ERROR_USER_MAPPED_FILE 1224), the cross-platform observation that the same 'file copy -force' succeeds on unix, and the candidate fixes with their trade-offs; THE AGENT DOES NOT SUBMIT IT - no account use, no web form, no API, no mail - submission is the developer's action alone, and the goal is not complete until the developer has submitted or declined; because TEMP_REFERENCE is ignored by both VCS and is one 'git clean -xdf' from gone, the reproducer and the source citations are ALSO carried verbatim in this tracked detail file, so the draft file is a convenience for pasting and never the only copy; once the developer submits, the ticket URL and the upstream response are recorded here with a disposition (fixed-in-release / declined / duplicate / superseded), and if fixed, the fixing release is named so the make.tcl workaround and the src/bootsupport/AGENTS.md warning can be dated; and the punkshell-side workaround is NOT removed by this goal - it must keep working on every runtime punkshell supports regardless of what upstream does.

Context

Found 2026-07-27 while diagnosing a make.tcl bootsupport failure that had recurred three times: replacing the zip-based punk::mix::templates modpod in src/bootsupport failed with

error copying "<src>" to "<dst>": invalid argument

The message is a dead end - it reads as a bad argument, and it names neither the real cause nor a remedy. Two earlier attempts at characterising it (recorded in commit messages and in goals/archive/G-124-punkzip-reader.md) were both wrong in different ways, precisely because the message carries no information.

The punkshell-side cause is understood and worked around (see the Finding below and src/bootsupport/AGENTS.md): a zipfs mount memory-maps its archive, and windows refuses to overwrite a file with a user-mapped section open. That part is ours and is fixed.

What remains is upstream's: Tcl converts EVERY windows error above 267 to EINVAL, so an entire class of real, common file-operation failures arrives as the same uninformative message. That is worth reporting, and this goal exists so that the report is drafted properly and the outcome is recorded - not so that an agent files it.

Finding (tracked copy - TEMP_REFERENCE is unversioned)

Verified 2026-07-27 against TEMP_REFERENCE/tcl9 (9.1b1) and TEMP_REFERENCE/androwish/jni/tcl (8.6.10). BOTH generations carry byte-identical logic.

win/tclWinError.c holds errorTable[], whose last entry is index 267 (ENOTDIR /* ERROR_DIRECTORY 267 */). The converter is:

void
Tcl_WinConvertError(unsigned errCode)
{
    if ((unsigned)errCode >= sizeof(errorTable)/sizeof(errorTable[0])) {
        errCode -= WSAEWOULDBLOCK;
        if ((unsigned)errCode >= sizeof(wsaErrorTable)/sizeof(wsaErrorTable[0])) {
            Tcl_SetErrno(errorTable[1]);
        } else {
            Tcl_SetErrno(wsaErrorTable[errCode]);
        }
    } else {
        Tcl_SetErrno(errorTable[errCode]);
    }
}

errorTable[1] is EINVAL (/* ERROR_INVALID_FUNCTION 1 */). So any windows error code above 267 that is not in the WSA range collapses to EINVAL -> "invalid argument". The affected set is not exotic:

303   ERROR_DELETE_PENDING          file is in the process of being deleted
1224  ERROR_USER_MAPPED_FILE        cannot be performed on a file with a user-mapped
                                    section open
1920  ERROR_CANT_ACCESS_FILE        the file cannot be accessed by the system
1921  ERROR_CANT_RESOLVE_FILENAME   the name of the file cannot be resolved
4390  ERROR_NOT_A_REPARSE_POINT     not a reparse point

The specific case that surfaced it: tclZipfs.c maps a mounted archive with CreateFileMappingW + MapViewOfFile (9.1b1: :1867 and :1875; UnmapViewOfFile at :1475), so any process holding a zipfs mount holds a user-mapped section on that file, and windows raises 1224 for an in-place overwrite.

Cross-platform observation worth including in the report: the same file copy -force over a memory-mapped file SUCCEEDS on unix. So the failure is windows-only and silent about it, which makes the misleading errno costlier than it first appears.

Measured behaviour, same machine, Tcl 9.0.3:

file copy -force over the archive, not mounted  -> ok
file copy -force over the archive, zipfs-mounted -> invalid argument
file copy -force over the archive, unmounted    -> ok
file delete of the archive, while mounted        -> ok        <-- unlink is permitted
delete-then-place while mounted                  -> ok, and the holder keeps
                                                    reading its own mapping

Reproducer (tracked copy)

Self-contained, no punkshell, windows only. Any zip will do - a kit or a zip-based .tm modpod is convenient.

set f mycopy.zip
file copy -force somearchive.zip $f
tcl::zipfs::mount $f probe
catch {file copy -force someotherfile $f} e
puts $e     ;# error copying ...: invalid argument
            ;# actual cause: ERROR_USER_MAPPED_FILE (1224), unrepresented in errorTable
tcl::zipfs::unmount probe

Expected after a fix: an errno that distinguishes this from a caller error - the message should not claim the arguments were invalid.

Approach

  • Search the tracker BEFORE drafting. This is old code present in 8.6 and 9.x, so a prior report is entirely plausible and finding one is a complete result for this goal.
  • Draft to TEMP_REFERENCE/tcl-windows-errno-catchall-TICKET-DRAFT.md in submission-ready form - a developer should be able to paste it without editing.
  • Keep the claim narrow and defensible: the REPORT is about the errno conversion, which is plainly a defect. The file copy -force portability difference is context in the same ticket, not a second demand - upstream may reasonably decline to change copy semantics (atomicity, ACL inheritance), and bundling it as a demand weakens the part that is clearly wrong.
  • Offer candidate fixes with trade-offs rather than insisting on one: extend the table; give the out-of-range branch a better default than EINVAL for the sharing/locking family (EACCES is defensible, EBUSY arguably closer); or expose the raw windows code so callers can diagnose. Deciding among these is upstream's call.

Alternatives considered

  • File it directly from an agent - rejected, and this is the point of the goal's shape: the developer submits. Agent-filed reports on a project's public tracker are the developer's reputation and correspondence to own, and a ticket needs someone who can answer follow-up questions.
  • Fix it locally and say nothing - rejected: the workaround is in punkshell's build tooling only, and every other Tcl user on windows keeps meeting "invalid argument" for a class of errors that has nothing to do with arguments.
  • Submit a patch rather than a report - deferred, not rejected. It is a table and the change is small, but choosing the right errno per class is a design conversation, and a report that names the candidates invites that conversation without presuming its outcome. If the developer prefers the patch aim, this goal's Acceptance changes rather than the finding.

Notes

  • PRECEDENT: G-039 (achieved 2026-07-12) filed a Tcl 9 windows console defect the same way - agent drafted the full text with repro scripts to TEMP_REFERENCE/, the developer submitted, and the goal recorded the URL (https://core.tcl-lang.org/tcl/tktview/f10d91c2d3). That draft elided the scripts from the web submission; whether to do the same here is the developer's call at submission time, not something this goal presumes.
  • The punkshell workaround this goal does NOT touch: ::punkboot::replace_possibly_mapped_file falls back from overwrite to delete-then-place, and ::punkboot::mapped_file_hint explains the catch-all when a copy genuinely fails. Both live in src/make.tcl and are documented in src/bootsupport/AGENTS.md. They must keep working on every runtime punkshell supports regardless of upstream, so an upstream fix dates them rather than deletes them.
  • Related: G-110 - it studies caching of EXTRACTED shared libs, where extraction today is done by zipfs/vfs::zip. Anything that holds a zipfs mount holds a mapping, so the same windows constraint applies to any scheme that wants to replace a mounted archive in place.
  • The invalid argument reading applies to ANY windows file operation in Tcl, not just copies. Treat it as "some windows error above 267" wherever it appears; that guidance is recorded in src/bootsupport/AGENTS.md because that is where it bit, but it is general.