Repository navigation
[flutter_tools] Safely handle broken symlinks and existing files during plugin symlink creation - #191496
Conversation
…ng plugin symlink creation In Dart, Link.existsSync() evaluates whether the link's destination target exists rather than checking if a symlink file entry exists on disk. If a plugin was removed, moved, or cleaned, Link.existsSync() returned false for broken symlinks. The tool then called Link.createSync(path), which crashed with ERROR_ALREADY_EXISTS (errno = 183) on Windows or EEXIST (errno = 17) on POSIX. This fix: 1. Uses typeSync(followLinks: false) to detect existing links and files type-agnostically in _createPlatformPluginSymlinks. 2. Removes stale, broken, or mismatched links and files prior to creating the new symlink. 3. Updates copyDirectory in file_system.dart to safely clean up conflicting destination entities before copying links or files. 4. Adds automated unit tests covering broken symlink recovery and existing file replacement. Fixes flutter#90028 Related to flutter#131978 Related to flutter#74227
…h pattern matching Uses Dart 3 pattern matching and switch statements on entity types to simplify conflict detection and file system traversal.
… symlink creation and directory copy
There was a problem hiding this comment.
Code Review
This pull request updates directory copying and platform plugin symlink creation to clean up conflicting destination entities and broken symlinks before writing new files or links. It also adds corresponding unit tests. The review feedback suggests performing conflict checks and deletions lazily in copyDirectory to prevent deleting existing destination files when copy filters return false, and using path canonicalization when comparing symlink targets to avoid platform-specific path mismatches.
…paths and scope changes to flutter_plugins - Use canonicalize on symlink target and path before comparison. - Revert copyDirectory modifications to keep the fix minimal and focused strictly on plugin symlink creation.
…latformPluginSymlinks
Is there a source for this, or an issue filed? This seems contrary to the documented behavior:
It can't possibly be following links before determining whether the link is the correct type since it would then never be true for links, but evaluating different files for the two steps seems extremely counterintuitive. I also wasn't able to repro that behavior in a trivial Dart script on macOS. |
Yeah, this isn't the right explanation. The real issue is when there's already existing file system entities that aren't symlinks, which causes This is the original exception and stack trace: #90028 (comment) Here's a reproduction script: import 'dart:io';
void main() {
final tempDir = Directory.systemTemp.createTempSync('flutter_issue_90028_repro_');
try {
final symlinkDir = Directory('${tempDir.path}/.plugin_symlinks')..createSync();
final pluginDir = Directory('${tempDir.path}/cache/some_plugin')..createSync(recursive: true);
// Scenario: A non-link file or directory already occupies the plugin path
// (e.g. from archive extraction, manual copy, git checkout, or previous builds).
final conflictingEntity = File('${symlinkDir.path}/some_plugin')
..writeAsStringSync('stale non-link file');
final link = Link('${symlinkDir.path}/some_plugin');
print('Conflicting file exists: ${conflictingEntity.existsSync()}');
print('link.existsSync() = ${link.existsSync()}');
// Original unfixed Flutter implementation from _createPlatformPluginSymlinks:
if (!link.existsSync()) {
print('link.existsSync() returned false. Calling link.createSync()...');
link.createSync(pluginDir.path);
}
} on FileSystemException catch (e) {
print('\n=== CRASH REPRODUCED ===');
print('Caught FileSystemException:');
print(' OS Error: ${e.osError}');
print(' Path: ${e.path}');
} finally {
tempDir.deleteSync(recursive: true);
}
}Output on Linux: I've updated the description and documentation to reflect the actual cause of the issue. |
|
autosubmit label was removed for flutter/flutter/191496, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label. |
flutter/flutter@c8c5e3b...0cbd1a4 2026-09-01 [email protected] Roll Skia from 6be7f8547c3c to 3911a1fe7f7a (1 revision) (flutter/flutter#192110) 2026-09-01 [email protected] Roll Fuchsia Linux SDK from EPzxifoyt36b5qiBy... to idslm9FikVLy2K_A-... (flutter/flutter#192104) 2026-09-01 [email protected] Roll Skia from 47df2ae3226c to 6be7f8547c3c (1 revision) (flutter/flutter#192103) 2026-09-01 [email protected] Roll Skia from e22ebf131e44 to 47df2ae3226c (4 revisions) (flutter/flutter#192099) 2026-09-01 [email protected] Roll Skia from 5489a16a5998 to e22ebf131e44 (5 revisions) (flutter/flutter#192093) 2026-09-01 [email protected] [flutter_tools] Safely handle broken symlinks and existing files during plugin symlink creation (flutter/flutter#191496) 2026-09-01 [email protected] Support wide gamut colors in gradient_generator's CreateGradientTexture (flutter/flutter#191980) 2026-09-01 [email protected] Clean up semantics code (flutter/flutter#191620) 2026-08-31 [email protected] Add website documentation item to PR pre-launch checklist (flutter/flutter#192080) 2026-08-31 [email protected] [windows] Fix null-deref in HostWindowPopup::UpdatePosition (Fixes #191478) (flutter/flutter#191479) 2026-08-31 [email protected] Removes deprecated ignoreSemantics parementers (flutter/flutter#191493) 2026-08-31 [email protected] Roll Dart SDK from b319095e317b to 9164def35347 (1 revision) (flutter/flutter#192073) 2026-08-31 [email protected] test: dynamic _tapOutside helper for TapRegion navigation tests (flutter/flutter#185397) 2026-08-31 [email protected] [Windows] fix: Remove quotes from compiler warning suppression (flutter/flutter#190873) 2026-08-31 [email protected] Migrate to listen package (flutter/flutter#189111) 2026-08-31 [email protected] Roll Skia from 15db98a90bbd to 5489a16a5998 (2 revisions) (flutter/flutter#192070) 2026-08-31 [email protected] Add regression test for plugin compileSdkExtension warning (flutter/flutter#191281) 2026-08-31 [email protected] Explicitly disable HCPP in platform view benchmarks and integration tests (flutter/flutter#191908) 2026-08-31 [email protected] Documentation and script for Gradle Distribution cache for CI (flutter/flutter#190323) 2026-08-31 [email protected] Roll Skia from 5549c93c9a1c to 15db98a90bbd (2 revisions) (flutter/flutter#192068) 2026-08-31 [email protected] Roll Packages from cd4cdd0 to d642322 (7 revisions) (flutter/flutter#192063) 2026-08-31 [email protected] Roll Dart SDK from 48f641e8b249 to b319095e317b (1 revision) (flutter/flutter#192061) 2026-08-31 [email protected] [tool] Migrate dev/tools from dart_skills_lint to skills_lint package (flutter/flutter#191997) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…r#12712) flutter/flutter@c8c5e3b...0cbd1a4 2026-09-01 [email protected] Roll Skia from 6be7f8547c3c to 3911a1fe7f7a (1 revision) (flutter/flutter#192110) 2026-09-01 [email protected] Roll Fuchsia Linux SDK from EPzxifoyt36b5qiBy... to idslm9FikVLy2K_A-... (flutter/flutter#192104) 2026-09-01 [email protected] Roll Skia from 47df2ae3226c to 6be7f8547c3c (1 revision) (flutter/flutter#192103) 2026-09-01 [email protected] Roll Skia from e22ebf131e44 to 47df2ae3226c (4 revisions) (flutter/flutter#192099) 2026-09-01 [email protected] Roll Skia from 5489a16a5998 to e22ebf131e44 (5 revisions) (flutter/flutter#192093) 2026-09-01 [email protected] [flutter_tools] Safely handle broken symlinks and existing files during plugin symlink creation (flutter/flutter#191496) 2026-09-01 [email protected] Support wide gamut colors in gradient_generator's CreateGradientTexture (flutter/flutter#191980) 2026-09-01 [email protected] Clean up semantics code (flutter/flutter#191620) 2026-08-31 [email protected] Add website documentation item to PR pre-launch checklist (flutter/flutter#192080) 2026-08-31 [email protected] [windows] Fix null-deref in HostWindowPopup::UpdatePosition (Fixes #191478) (flutter/flutter#191479) 2026-08-31 [email protected] Removes deprecated ignoreSemantics parementers (flutter/flutter#191493) 2026-08-31 [email protected] Roll Dart SDK from b319095e317b to 9164def35347 (1 revision) (flutter/flutter#192073) 2026-08-31 [email protected] test: dynamic _tapOutside helper for TapRegion navigation tests (flutter/flutter#185397) 2026-08-31 [email protected] [Windows] fix: Remove quotes from compiler warning suppression (flutter/flutter#190873) 2026-08-31 [email protected] Migrate to listen package (flutter/flutter#189111) 2026-08-31 [email protected] Roll Skia from 15db98a90bbd to 5489a16a5998 (2 revisions) (flutter/flutter#192070) 2026-08-31 [email protected] Add regression test for plugin compileSdkExtension warning (flutter/flutter#191281) 2026-08-31 [email protected] Explicitly disable HCPP in platform view benchmarks and integration tests (flutter/flutter#191908) 2026-08-31 [email protected] Documentation and script for Gradle Distribution cache for CI (flutter/flutter#190323) 2026-08-31 [email protected] Roll Skia from 5549c93c9a1c to 15db98a90bbd (2 revisions) (flutter/flutter#192068) 2026-08-31 [email protected] Roll Packages from cd4cdd0 to d642322 (7 revisions) (flutter/flutter#192063) 2026-08-31 [email protected] Roll Dart SDK from 48f641e8b249 to b319095e317b (1 revision) (flutter/flutter#192061) 2026-08-31 [email protected] [tool] Migrate dev/tools from dart_skills_lint to skills_lint package (flutter/flutter#191997) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…r#12712) flutter/flutter@c8c5e3b...0cbd1a4 2026-09-01 [email protected] Roll Skia from 6be7f8547c3c to 3911a1fe7f7a (1 revision) (flutter/flutter#192110) 2026-09-01 [email protected] Roll Fuchsia Linux SDK from EPzxifoyt36b5qiBy... to idslm9FikVLy2K_A-... (flutter/flutter#192104) 2026-09-01 [email protected] Roll Skia from 47df2ae3226c to 6be7f8547c3c (1 revision) (flutter/flutter#192103) 2026-09-01 [email protected] Roll Skia from e22ebf131e44 to 47df2ae3226c (4 revisions) (flutter/flutter#192099) 2026-09-01 [email protected] Roll Skia from 5489a16a5998 to e22ebf131e44 (5 revisions) (flutter/flutter#192093) 2026-09-01 [email protected] [flutter_tools] Safely handle broken symlinks and existing files during plugin symlink creation (flutter/flutter#191496) 2026-09-01 [email protected] Support wide gamut colors in gradient_generator's CreateGradientTexture (flutter/flutter#191980) 2026-09-01 [email protected] Clean up semantics code (flutter/flutter#191620) 2026-08-31 [email protected] Add website documentation item to PR pre-launch checklist (flutter/flutter#192080) 2026-08-31 [email protected] [windows] Fix null-deref in HostWindowPopup::UpdatePosition (Fixes #191478) (flutter/flutter#191479) 2026-08-31 [email protected] Removes deprecated ignoreSemantics parementers (flutter/flutter#191493) 2026-08-31 [email protected] Roll Dart SDK from b319095e317b to 9164def35347 (1 revision) (flutter/flutter#192073) 2026-08-31 [email protected] test: dynamic _tapOutside helper for TapRegion navigation tests (flutter/flutter#185397) 2026-08-31 [email protected] [Windows] fix: Remove quotes from compiler warning suppression (flutter/flutter#190873) 2026-08-31 [email protected] Migrate to listen package (flutter/flutter#189111) 2026-08-31 [email protected] Roll Skia from 15db98a90bbd to 5489a16a5998 (2 revisions) (flutter/flutter#192070) 2026-08-31 [email protected] Add regression test for plugin compileSdkExtension warning (flutter/flutter#191281) 2026-08-31 [email protected] Explicitly disable HCPP in platform view benchmarks and integration tests (flutter/flutter#191908) 2026-08-31 [email protected] Documentation and script for Gradle Distribution cache for CI (flutter/flutter#190323) 2026-08-31 [email protected] Roll Skia from 5549c93c9a1c to 15db98a90bbd (2 revisions) (flutter/flutter#192068) 2026-08-31 [email protected] Roll Packages from cd4cdd0 to d642322 (7 revisions) (flutter/flutter#192063) 2026-08-31 [email protected] Roll Dart SDK from 48f641e8b249 to b319095e317b (1 revision) (flutter/flutter#192061) 2026-08-31 [email protected] [tool] Migrate dev/tools from dart_skills_lint to skills_lint package (flutter/flutter#191997) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Description
Fixes a crash during plugin symlink creation on Windows/Linux desktop builds (
FileSystemException: Creation failed, OS Error: A file cannot be created if it already exists., errno = 183/errno = 17).Cause
Previously,
_createPlatformPluginSymlinkscheckedif (link.existsSync()) continue;before callinglink.createSync(path).Link.existsSync()returnedfalse. The tool then calledlink.createSync(path), crashing withERROR_ALREADY_EXISTS(errno = 183) on Windows orEEXIST(errno = 17) on POSIX.Link.existsSync()did not check if an existing symlink pointed to the expected plugin target path. If a plugin's path changed (e.g. a version upgrade in pub cache), existing symlinks pointing to outdated paths were skipped.Fix
typeSync(link.path, followLinks: false)to inspect disk state type-agnostically before symlink creation.link.targetSync()withpath. If matching, creation is skipped.ErrorHandlingFileSystem.deleteIfExists(link), and removes conflicting non-link entries before callinglink.createSync(path).Related Issues
Tests
packages/flutter_tools/test/general.shard/plugins_test.dart:createPluginSymlinks repairs broken symlinks without failingcreatePluginSymlinks replaces existing files with symlinks without failing