apache/flink · error · FlinkException

The discovery script %s is not executable.

Error message

The discovery script %s is not executable.

What it means

Thrown by GPUDriver when the GPU discovery script file exists but the JVM sees it as non-executable (File.canExecute() == false). This is a filesystem permission check done after the existence check in the constructor, so it only fires once the path itself is correct.

Source

Thrown at flink-external-resources/flink-external-resource-gpu/src/main/java/org/apache/flink/externalresource/gpu/GPUDriver.java:89

        }

        Path discoveryScriptPath = Paths.get(discoveryScriptPathStr);
        if (!discoveryScriptPath.isAbsolute()) {
            discoveryScriptPath =
                    Paths.get(
                            System.getenv().getOrDefault(ConfigConstants.ENV_FLINK_HOME_DIR, "."),
                            discoveryScriptPathStr);
        }
        discoveryScriptFile = discoveryScriptPath.toFile();

        if (!discoveryScriptFile.exists()) {
            throw new FileNotFoundException(
                    String.format(
                            "The gpu discovery script does not exist in path %s.",
                            discoveryScriptFile.getAbsolutePath()));
        }
        if (!discoveryScriptFile.canExecute()) {
            throw new FlinkException(
                    String.format(
                            "The discovery script %s is not executable.",
                            discoveryScriptFile.getAbsolutePath()));
        }

        args = config.get(DISCOVERY_SCRIPT_ARG);
    }

    @Override
    public Set<GPUInfo> retrieveResourceInfo(long gpuAmount) throws Exception {
        Preconditions.checkArgument(
                gpuAmount > 0,
                "The gpuAmount should be positive when retrieving the GPU resource information.");

        final Set<GPUInfo> gpuResources = new HashSet<>();
        String output = executeDiscoveryScript(discoveryScriptFile, gpuAmount, args);
        if (!output.isEmpty()) {
            String[] indexes = output.split(",");

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Run 'chmod +x <script>' on every TaskManager node (see the absolute path in the error message)
  2. For Kubernetes ConfigMaps, set defaultMode to 0755 on the volume mount
  3. Copy files with 'cp -p' or 'rsync -p' / extract with 'tar -p' to preserve the execute bit
  4. Verify with 'sudo -u <flink-user> test -x <script> && echo ok'

Example fix

# before
$ ls -l /opt/flink/scripts/gpu-discovery.sh
-rw-r--r-- 1 flink flink 512 ...

# after
$ chmod +x /opt/flink/scripts/gpu-discovery.sh
$ ls -l /opt/flink/scripts/gpu-discovery.sh
-rwxr-xr-x 1 flink flink 512 ...
Defensive patterns

Strategy: validation

Validate before calling

java.io.File f = new java.io.File(scriptPath);
if (f.exists() && !f.canExecute()) {
    throw new IllegalStateException("Make the GPU discovery script executable: chmod +x " + f.getAbsolutePath());
}

Prevention

When it happens

Trigger: Constructing GPUDriver with a discovery script whose execute bit is not set for the user running the TaskManager JVM: chmod missing, file copied without preserving permissions, or a read-only mount (some Docker volumes, ConfigMaps in Kubernetes) that drops the +x bit.

Common situations: Kubernetes ConfigMap volumes mount files as 0644 by default; extracting a tarball as root and running Flink as another user; scripts edited/re-uploaded via a tool that resets permissions.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/23b2812916ace418. Report an issue: GitHub.