Skip to content

[v3, windows] Fix generate bindings failing with "Access is denied" when Vite dev server is running#5561

Merged
leaanthony merged 3 commits into
masterfrom
fix/5515-bindings-rename-windows
Jun 10, 2026
Merged

[v3, windows] Fix generate bindings failing with "Access is denied" when Vite dev server is running#5561
leaanthony merged 3 commits into
masterfrom
fix/5515-bindings-rename-windows

Conversation

@leaanthony

@leaanthony leaanthony commented Jun 10, 2026

Copy link
Copy Markdown
Member

Description

Fixes #5515 — a regression introduced in v3.0.0-alpha.96 by #5367.

The atomic directory swap from #5367 (os.RemoveAll + os.Rename) fails on Windows when a file watcher such as Vite's dev server is running: the watcher holds an open handle on the watched bindings directory (via ReadDirectoryChangesW), so deleting it leaves the name in delete-pending state and the subsequent rename over it is rejected with Access is denied.

The fix

Bindings are still generated into a dot-prefixed sibling temp directory (invisible to chokidar during generation, as before), but the result is now installed by syncing files into the output directory instead of replacing the directory itself:

If the output directory doesn't exist at all, the whole tree is still moved with a single rename (fast path).

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • New unit tests for syncDirs covering: no pre-existing destination, update+stale-delete with unchanged files verified untouched (mtime preserved), file↔directory type mismatches, and fully identical content.
  • End-to-end on macOS: built wails3 and ran generate bindings -clean against v3/examples/binding twice — second run over existing output with planted stale files/dirs. Stale entries were removed, unchanged files kept their original mtimes (i.e. were not rewritten), and no temp directories were left behind.
  • go test ./internal/commands/ passes; go vet clean.

The Windows-with-Vite-running scenario from the issue would be good to confirm on a Windows machine, but the failing operation (rename over a watched directory) is structurally eliminated rather than retried around.

Checklist:

  • My code follows the general coding style of this project
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated v3/UNRELEASED_CHANGELOG.md

Summary by CodeRabbit

  • Bug Fixes

    • wails3 generate bindings no longer fails on Windows with "Access is denied" when the Vite dev server is running; bindings are now synced into the output to avoid watcher/rename issues and improve reliability.
  • Tests

    • Added comprehensive cross-platform tests for bindings synchronization to ensure correct updates, deletions, type reconciliation, and idempotence.

…e output directory (#5515)

The atomic directory swap introduced in #5367 (RemoveAll+Rename) fails on
Windows with "Access is denied" when a file watcher such as Vite's dev
server is running: the watcher holds an open handle on the watched
bindings directory, so deleting it leaves the name in delete-pending
state and the subsequent rename over it is rejected.

Keep generating into the dot-prefixed temp directory (still invisible to
chokidar during generation), but install the result by syncing files into
the output directory instead of replacing the directory itself:

- the output directory is never deleted or renamed, so Windows watcher
  handles are never violated (#5515)
- the directory delete+recreate cascade that caused the chokidar
  rename-event loop still never happens (#3976)
- unchanged files are left untouched, so the dev server only reloads
  files that actually changed

File operations retry briefly with backoff on Windows to ride out
transient locks from watchers and antivirus scanners.

Fixes #5515
Copilot AI review requested due to automatic review settings June 10, 2026 10:18
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 563fb06d-233c-4982-8ada-0eb6b2905d57

📥 Commits

Reviewing files that changed from the base of the PR and between 4f38b0a and 3a30f00.

📒 Files selected for processing (3)
  • v3/internal/commands/bindings.go
  • v3/internal/commands/bindings_sync.go
  • v3/internal/commands/bindings_sync_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • v3/internal/commands/bindings.go
  • v3/internal/commands/bindings_sync_test.go
  • v3/internal/commands/bindings_sync.go

Walkthrough

This PR replaces the atomic directory swap (rename) in bindings generation with a file-by-file synchronization approach (syncDirs), adds Windows-aware retry/backoff, integrates it into GenerateBindings, adds tests, and records a changelog entry for the Windows fix.

Changes

Bindings sync implementation and integration

Layer / File(s) Summary
File synchronization utility with retry logic
v3/internal/commands/bindings_sync.go
syncDirs(src, dst) mirrors source into destination by fast-path rename when dst is absent, or by walking src to copy/replace differing entries, skipping identical regular files, resolving type mismatches, removing stale dst entries, and finally deleting src. Helpers: replaceFile, sameFileContent, and withRetry (Windows-only exponential backoff).
Synchronization test suite
v3/internal/commands/bindings_sync_test.go
Test helpers writeFiles, readTree, checkTree and five test cases verifying: destination creation, updates+deletes+mtime preservation, file-vs-directory type mismatch reconciliation, handling destination-as-file, and identical-content no-op.
Bindings generation integration
v3/internal/commands/bindings.go
GenerateBindings now generates into a dot-prefixed temp sibling and calls syncDirs(generationDir, absPath) instead of RemoveAll+Rename; also changes mkdir mode to 0o755 and updates error messaging to mention possible partial updates.
Changelog entry
v3/UNRELEASED_CHANGELOG.md
Adds a Fixed entry describing that wails3 generate bindings no longer fails with "Access is denied" on Windows when Vite dev server is running (syncs generated files instead of renaming).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • wailsapp/wails#5367: Related work that also writes to a dot-prefixed temp directory to avoid chokidar/Vite rename loops; this PR further replaces the final swap with file-by-file sync.

Suggested labels

size:XL, lgtm

Suggested reviewers

  • fbbdev

Poem

🐰 I hopped through code to mend the bind,
Swapped renames for syncs, a gentler kind.
No more "Denied!" from Windows' gate,
Vite can watch—no furious state.
Hooray, small files dance into place.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: preventing generate bindings from failing with 'Access is denied' on Windows when Vite dev server is running.
Description check ✅ Passed The description provides comprehensive context including issue reference, root cause, solution approach, testing details, and completed checklist items, addressing all key template sections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/5515-bindings-rename-windows

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Windows-specific regression where wails3 generate bindings fails with “Access is denied” when the bindings directory is being watched (e.g. by Vite/chokidar). It replaces the previous “atomic directory swap” install step with a per-file sync into the existing output directory to avoid deleting/renaming a watched directory.

Changes:

  • Install generated bindings by syncing files into the existing output directory (instead of RemoveAll + Rename over the directory) to avoid Windows watcher handle conflicts.
  • Add syncDirs utility with Windows retry/backoff for transient file locks.
  • Add unit tests covering update/delete behavior, type mismatches, and “identical content leaves mtimes untouched”.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
v3/UNRELEASED_CHANGELOG.md Adds a changelog entry for the Windows “Access is denied” bindings fix.
v3/internal/commands/bindings.go Switches bindings installation step from directory swap to syncDirs(...).
v3/internal/commands/bindings_sync.go Implements directory syncing logic + Windows retry/backoff helper.
v3/internal/commands/bindings_sync_test.go Adds unit tests for the sync behavior and mtime preservation for unchanged files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v3/internal/commands/bindings_sync.go
Comment thread v3/internal/commands/bindings_sync.go
Comment thread v3/internal/commands/bindings_sync.go Outdated
Comment thread v3/internal/commands/bindings_sync.go Outdated
- Handle the output path existing as a regular file: clear it and move
  the generated tree into place (with test)
- Fall back to remove+rename in replaceFile for destinations that
  os.Rename cannot replace on Windows (read-only attribute)
- Retry the final temp directory removal like other file operations
- Use 0o755 for created directories, consistent with the rest of the
  package
@leaanthony leaanthony merged commit aceac48 into master Jun 10, 2026
119 checks passed
@leaanthony leaanthony deleted the fix/5515-bindings-rename-windows branch June 10, 2026 11:06
leaanthony added a commit that referenced this pull request Jun 13, 2026
Bundles content from the failed alpha.99 release (tag pushed but no
GitHub release created due to token misconfiguration) together with
the alpha.100 cycle. Includes:
- MacWebviewPreferences WKWebView extensions (#5549)
- generate bindings "Access is denied" fix on Windows (#5561)
- Linux frameless JS resize / scrollbar edge detection fix (#5368)
- Windows updater cross-volume rename fallback (#5560)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[V3, Windows] generate bindings fails with "Access is denied" on Windows when Vite dev server is running (v3.0.0-alpha.96)

2 participants