⚡ Sangsakala Proxy 🏠 Home
Skip to content

[windows] file-system-access: async ops invoked on pending IAsyncOperation; enumeration/size/sync members broken #11613

Open

Description

@aleclarson

Environment

@nativescript/core 9.1.3 (pkg.pr.new runtime abae298/cbec98bb), packaged WinUI 3 MSIX (org.octanexplat.windows61), Windows 11 x64, {N} Windows runtime alpha.

Defects verified on the packaged runtime

packages/core/file-system/file-system-access.windows.js has two defect classes, verified at runtime (not source inspection):

1. Async methods invoked on the pending IAsyncOperation (E_FAIL)

deleteFile, deleteFolder, and rename call StorageFile.GetFileFromPathAsync(path) / StorageFolder.GetFolderFromPathAsync(path) synchronously, then invoke DeleteAsync()/RenameAsync() on the returned operation object rather than the resolved entity:

// deleteFile (windows):
StorageFile.GetFileFromPathAsync(path).DeleteAsync()  // pending op, not the file

Runtime result: File.remove() rejects Error: Unspecified error (HRESULT 0x80004005) and removeSync() silently swallows it — deletes never happen. Verified on MSIX 1.0.0.204/205/209/212; workaround is toPromise(GetFileFromPathAsync(path)) then toPromise(file.DeleteAsync()), which deletes correctly.

2. Fake-sync .done() pattern on a projection that only exposes then

  • Folder.getEntities/eachEntity → StorageFolder.GetFolderFromPathAsync(...).done is not a function (the code calls op.done(...)).
  • getFileSize → file.size silently returns 0.
  • readTextSync/writeTextSync/readBufferSync/writeBufferSync → call .done() and return before completion, so sync reads always yield ''/undefined.
  • File.lastModified resolves but the try/catch fallback can hide a real failure behind new Date().

Net working surface on the pinned runtime: File.exists, Folder.exists, path joins, and the real async text/buffer IO (readText/writeText/readBuffer/writeBuffer/appendText). Every delete, rename, enumeration, size, and sync-read member is broken or silent-wrong.

Happy to share the qualification fixture that produced the evidence.

Activity

  1. aleclarson commented on Nov 4, 2026

    @aleclarson
    ContributorAuthor

    Real-world impact — the broken members sit in the standard {N} file API that ordinary app code uses everywhere:

    • Cache eviction silently never happens. file.remove()/removeSync() throwing (or being swallowed) means thumbnail/media/document caches grow unboundedly — any app doing "delete the stale cached file" leaks disk on every run.
    • file.tmp → file.ext download/rename fails. Download managers and save flows that write to a temp name then rename() to the final name — a near-universal pattern — break with E_FAIL.
    • Directory listing is dead. Folder.getEntities()/eachEntity() is how apps show "recent files", enumerate a documents/exports folder, or sum cache sizes. It throws on call.
    • file.size returning 0 breaks progress and quota checks. Download/upload progress (% of file), "file too large" validation, and storage-usage displays all silently report wrong data — worse than an error, since it looks like a successful 0.
    • readTextSync returning '' silently corrupts config reads. A quick sync settings/feature-flag read yields an empty string rather than throwing, so callers proceed with empty data as if the file were blank.

    Effectively: any {N} app that lists a folder, deletes a temp/cache file, renames a downloaded file, or reads file size hits a wall on Windows today — the only safe paths are exists, joins, and the async text/buffer methods.

  2. self-assigned this
    on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions