fix(builder): a game dump Windows refuses to read says why and what to do, not "Permission denied" #641
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!641
Loading…
Reference in a new issue
No description provided.
Delete branch "llun/Longshot:fix/nup-read-error"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: fix · Risk: low · Tested on: Windows 11 (the build reading the game files; no GPU involved) · Needs: none
Why
A player's build can stop at "Read the game files" with only
nup.py stopped (exit status 1): PermissionError: [Errno 13] Permission denied. It reads like a folder permission problem, but it isn't one: Windows opened the game dump fine and thenrefused to read one part of it. That happens when another program holds a lock on that part of the file, or when the
drive can't read it back (a bad spot on a disk, a USB drive that drops out). The player gets no hint of either, or of
what to do. See #295, where a player hit this partway through a 25.7 GB dump.
How to reproduce
Seen on: Windows 11 (any GPU), the launcher building nightly 170 (main @
6c9edffe), and v3.0.0-rc.3 in #295Settings and switches: defaults
Steps: have another process hold an exclusive byte-range lock (
LockFileEx) on part of the .nup's romfs data, thenbuild from that .nup in the launcher
Expected: a message that says the file is locked by another program and what to do
Actual:
nup.py stopped (exit status 1): PermissionError: [Errno 13] Permission deniedWhat changed
When the system refuses a read of the .nup's data,
nup.pynow stops with a message that names the file and the pointin it, says why (another program has that part locked, or the drive couldn't read it), and what to do. It gets the real
reason from the Windows error the C runtime kept, which Python's "Errno 13" hides.
Nup._data()is the one read of the package's data (inner_is_zstd,is_packed,copy_to). On anOSErrorit raisesSystemExit(unreadable(...)).windows_error()reads the error with the C runtime's_get_doserrno, which is looked up when nup.py loads: importing ctypes after the failure ran file calls of its ownthat replaced the error.
unreadable()gives one of three messages:the .nup to another folder and build from the copy
drive, and if Windows can't copy it either, check the drive or dump the game again onto another drive
The header and TOC reads when the package opens are unchanged. Errors from writing the extracted files (the full
drive in
out_of_space()) still reachextract()as they did.The message names the file, not its folder. On Windows, a real lock held by another process gives the lock message
with error 33.
Measurements
Nothing to measure: only the message of a failed read of the dump changes (a full read of a real dump through the changed reads matches main, see Testing).
Flags and settings
None
Testing
Tested:
at 247 MB (a romfs file early in the extraction). Main's
nup.pystops withPermissionError: [Errno 13] Permission denied. This PR'snup.pystops with "Windows would not let the build read <the .nup> at 247 of 24435 MB: anotherprogram has that part of the file locked (Windows error 33). ..." The screenshots are in the first comment.
nup.py --verify --title 1): 186,588 records, 159,537 uniqueextents, 0 bad, about 20 s, the same as main's
nup.pyon it.python -I -B tests/run_all.pyon this PR @0f7d98dd(main @f5718d03), Python 3.15 with llvm-mingw on PATH:491 passed, 18 failed, 79 skipped. Main @
f5718d03on this PC: 491 passed, 17 failed, the same 17 (main's own here:test_appimage_player_build, test_bake_portability, test_cpm_discovery, test_crash_dialog, test_env_on,
test_fpx_sticky, test_lift_cache, test_lift_cleanup_error, test_linux_preflight, test_make_game_cache,
test_oot_state_tap, test_readback_late, test_spawn_emit, test_tested_configurations, test_vk_pgo,
test_vram_budget_row, test_walk_key). The 18th,
tests/test_longshot_ctl.py, timed out a 1 s log wait(
Sessions.test_utf8_cursor_keeps_incomplete_characters) under the full run's load; alone it passes 3 of 3 here andon main, and this PR doesn't touch it. The new check passes.
nx2/test_nup_read_error.pyfails on main (the refused read comes out as the bareOSError) and passes here. Alleight
nx2/test_nup_*.pychecks pass.Not tested:
its message through the Windows error numbers and Linux's EIO.
the same reads there.
Must be tested: None
Risks
What could break: a build from a .nup on another OS or Python, if
Nup._data()changed an ordinary read. It doesn't:it is the same
read(), withtell()before it. The full verify above reads every file of a dump through it.Turning it off: revert the commit
Compatibility: Nothing changes: saves, the options file, the caches, the vk ABI and the builder's tools are as they
were. Only the message of a failed read differs.
Issues
See #295
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.🤖 Generated with Claude Code
Screenshots: the launcher building nightly 170's tools from a real dump while another process locks 1 MB of it at 247 MB. Only
nx2/nup.pydiffers between the two.Before: main @
6c9edffeAfter: this PR @
543459cbWIP: fix(builder): a game dump Windows refuses to read says why and what to do, not "Permission denied"to fix(builder): a game dump Windows refuses to read says why and what to do, not "Permission denied"543459cbb57baf5b76667baf5b76660f7d98ddf1Rebased onto main @
f5718d03(#629): the head is now0f7d98dd. No conflicts, and the patch is unchanged (it replaces7baf5b76, the same commit onb80a68f1).python -I -B tests/run_all.pyon0f7d98dd, Windows 11: 491 passed, 18 failed, 79 skipped. Main @f5718d03on the same PC: 491 passed, 17 failed, the same 17 (named in Testing). The 18th,tests/test_longshot_ctl.py, timed out a 1 s log wait under the full run's load and passes 3 of 3 alone, here and on main.nx2/test_nup_read_error.pypasses. The Testing section now gives this run instead of the one on6c9edffe.Open PRs that touch
nx2/nup.py(#218, #546) change other parts of it; ours merges cleanly with both.