You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[hooks_runner] TimeoutException when file locking is unsupported by the filesystem (e.g. SMB shares on macOS) #3651
When running builds (such as flutter build ios or flutter run) on a project hosted on a filesystem where advisory byte-range file locking is unsupported (such as macOS SMB/smbfs network shares), hooks_runner fails after 5 minutes with a TimeoutException:
Could not acquire the lock to /Volumes/APP/.dart_tool/hooks_runner/shared/objective_c/.lock.
Target build_hooks failed: TimeoutException after 0:05:00.000000: Could not acquire the lock to /Volumes/APP/.dart_tool/hooks_runner/shared/objective_c/.lock.
Failed to package /Volumes/APP.
Command PhaseScriptExecution failed with a nonzero exit code
Root Cause
In pkgs/hooks_runner/lib/src/locking/locking.dart, _runUnderFileLock attempts to lock .lock:
while (timeout ==null|| stopwatch.elapsed < timeout) {
try {
await randomAccessFile.lock(FileLock.exclusive);
...
} onFileSystemException {
if (errorFromCallback) {
rethrow;
}
if (!printed) {
logger?.finer(
'Waiting to be able to obtain lock of directory: ${file.path}.',
);
printed =true;
}
// Don't busy wait, give the CPU some rest.// Magic constant taken from flutter_tools for startup lock.awaitFuture<void>.delayed(constDuration(milliseconds:50));
}
}
final message ='Could not acquire the lock to ${file.path}.';
logger?.severe(message);
throwTimeoutException(message, timeout);
On POSIX systems, randomAccessFile.lock() delegates to fcntl(fd, F_SETLK, &fl).
When the underlying filesystem does not support advisory locking (such as macOS SMB mounts, NFS, or special virtual filesystems), fcntl returns an OS error code such as ENOTSUP (45), ENOLCK (77), or EINVAL (22). Dart surfaces this as a native FileSystemException with an OSError.
_runUnderFileLock catches all FileSystemException instances and unconditionally treats them as lock contention, sleeping for 50ms and retrying until the 5-minute timeout expires.
Proposed Fix
Inspect e.osError?.errorCode in _runUnderFileLock. If the error code indicates that locking is unsupported rather than genuine contention (e.g., outside Linux EAGAIN 11, POSIX EACCES 13, macOS EAGAIN 35, Windows sharing/lock violations 32/33):
Log that locking is unsupported on the current filesystem.
Proceed to execute callback() without holding the lock rather than timing out.
Proceed to execute callback() without holding the lock rather than timing out.
This will lead to race conditions between hook invocations. Hook authors can then no longer rely on that they are not invoked concurrently for the same input configuration. Which would then lead to invoking clang/xcode/msvc/CMake or downloading the same dylib on the same dir concurrently. Pushing the problem to hook authors.
Some wild ideas to do cross-process mutex without process file locks:
Acquiring and holding a port open, e.g. localhost:3843
Existence of a file on disk, if you created it you have the lock, and you update the contents each minute. If the file on disk is older than 2 minutes assume the process owning it died and allow to create it.
Activity
dcharkes commentedon Sep 16, 2026
Thanks for the report @bkonyi 🙏
This will lead to race conditions between hook invocations. Hook authors can then no longer rely on that they are not invoked concurrently for the same input configuration. Which would then lead to invoking clang/xcode/msvc/CMake or downloading the same dylib on the same dir concurrently. Pushing the problem to hook authors.
Some wild ideas to do cross-process mutex without process file locks:
localhost:3843[hooks_runner] Fail eagerly when file locking is not supported on fil…
[hooks_runner] Fail eagerly when file locking is not supported on fil…
[hooks_runner] Fail eagerly when file locking is not supported on fil…