elastic/elasticsearch · error · LinkCreationException

Failed to create hard link {} pointing to {}

Error message

Failed to create hard link {} pointing to {}

What it means

Thrown by syncWithLinks when Files.createLink(d, s) fails with IOException. The plugin uses hard links to share the extracted distribution across multiple nodes without duplicating files (saves disk + keeps config immutable). The comment notes this does not work for network drives (e.g. Vagrant shared folders).

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:1329

            }
        }
    }

    /**
     * Does the equivalent of `cp -lr` and `chmod -r a-w` to save space and improve speed.
     * We remove write permissions to make sure files are note mistakenly edited ( e.x. the config file ) and changes
     * reflected across all copies. Permissions are retained to be able to replace the links.
     *
     * @param sourceRoot      where to copy from
     * @param destinationRoot destination to link to
     */
    private void syncWithLinks(Path sourceRoot, Path destinationRoot) {
        sync(sourceRoot, destinationRoot, (Path d, Path s) -> {
            try {
                Files.createLink(d, s);
            } catch (IOException e) {
                // Note does not work for network drives, e.g. Vagrant
                throw new LinkCreationException("Failed to create hard link " + d + " pointing to " + s, e);
            }
        });
    }

    private void syncWithCopy(Path sourceRoot, Path destinationRoot) {
        sync(sourceRoot, destinationRoot, (Path d, Path s) -> {
            try {
                Files.copy(s, d);
            } catch (IOException e) {
                throw new UncheckedIOException("Failed to copy " + s + " to " + d, e);
            }
        });
    }

    private void sync(Path sourceRoot, Path destinationRoot, BiConsumer<Path, Path> syncMethod) {
        assert Files.exists(destinationRoot) == false;
        try {
            Files.walkFileTree(sourceRoot, new SimpleFileVisitor<>() {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Move the project and gradle home (GRADLE_USER_HOME) onto a local filesystem that supports hard links (ext4, APFS, NTFS).
  2. If on a Mac with a case-insensitive volume or an external drive, build from the internal SSD.
  3. For Vagrant/Docker, use a native synced-folder type (rsync/nfs vs virtualbox shared), or build outside the shared folder.
  4. If you cannot change the filesystem, you must not use this code path — file an issue; there is no in-process fallback to copy.

Example fix

// before: workingDir on a network share
// gradle.properties:
//   gradle.userHomeDir=/network/share/gradle-home
// after: point gradle home at local disk
//   gradle.userHomeDir=/Users/me/.gradle
Defensive patterns

Strategy: validation

Validate before calling

try {
    Files.createLink(Path.of("probe-link"), Path.of("probe-target"));
    Files.deleteIfExists(Path.of("probe-link"));
} catch (IOException e) {
    // filesystem does not support hard links — fail fast with a clear message
}

Try / catch

try {
    Files.createLink(d, s);
} catch (IOException e) {
    throw new LinkCreationException("Failed to create hard link " + d + " pointing to " + s, e);
}

Prevention

When it happens

Trigger: Files.createLink throws when: the source and destination are on different filesystems/volumes (EXDEV), the filesystem does not support hard links (some network filesystems, FAT/exFAT, some FUSE mounts), the destination already exists, or permissions are insufficient.

Common situations: The gradle build dir lives on a network share (NFS, SMB, Vagrant synced folder, Docker volume on a non-native driver). The user moved their gradle home or project onto a separate volume from the extracted distro.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/a8f3873264d0ee73. Report an issue: GitHub.