github/copilot-sdk · error · IOException

Failed to make Copilot CLI executable:

Error message

Failed to make Copilot CLI executable: 

What it means

makeExecutable calls File.setExecutable(true, false) on a freshly written binary. If a SecurityException is raised, this IOException wrapping the path is thrown. It means the JVM was not permitted to set the executable bit — typically a Java SecurityManager policy restriction or an OS-level ACL denying chmod on the target.

Solutions

  1. Grant the running code FilePermission(read,write,execute) on the cache directory in the security policy.
  2. Point the cache directory at a user-writable location (e.g. -Dcopilot.cache.dir=$HOME/.cache/copilot).
  3. Remove or relax the SecurityManager/sandbox restrictions if policy allows.
  4. Check directory ACLs (icacls / chmod / ls -le) and fix ownership so the running user can chmod files.

Example fix

// before (policy file)
grant { permission java.io.FilePermission "/opt/copilot-cache/-", "read"; };
// after
grant { permission java.io.FilePermission "/opt/copilot-cache/-", "read,write,execute,delete"; };
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: confirm the JVM may write+execute in the cache dir before loading
Path probe = cacheDir.resolve(".probe");
Files.writeString(probe, "x");
probe.toFile().setExecutable(true, false);
Files.deleteIfExists(probe);

Try / catch

try { loader.loadRuntime(); } catch (IOException e) { if (e instanceof SecurityException || e.getMessage().startsWith("Failed to make Copilot CLI executable")) { // widen SecurityManager policy or switch cache dir } else throw e; }

Prevention

When it happens

Trigger: A SecurityManager (or sandboxed embedding) denies FilePermission 'execute'/'write' for the cache path when resolving the runtime wrapper or extracting runtime assets/CLI.

Common situations: Application servers or containerized environments running with a SecurityManager and restrictive policy; read-only or ACL-locked cache directories; macOS/Windows ACLs on company-managed machines.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/43b199e5d1811814. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java:574

        if (!Files.isRegularFile(path)) {
            return false;
        }
        return Files.size(path) > 0;
    }

    private static boolean isValidCachedCli(Path path) throws IOException {
        return isValidCachedFile(path) && (isWindows() || Files.isExecutable(path));
    }

    private static void makeExecutable(Path path) throws IOException {
        if (isWindows()) {
            return;
        }
        final boolean executableSet;
        try {
            executableSet = path.toFile().setExecutable(true, false);
        } catch (SecurityException ex) {
            throw new IOException("Failed to make Copilot CLI executable: " + path, ex);
        }
        if (!executableSet || !Files.isExecutable(path)) {
            throw new IOException("Failed to make Copilot CLI executable: " + path);
        }
    }

    private static void copyResourceToTemp(URL resource, String resourcePath, Path temp) throws IOException {
        try (InputStream in = resource.openStream()) {
            long bytesWritten = Files.copy(in, temp, StandardCopyOption.REPLACE_EXISTING);
            if (bytesWritten == 0) {
                throw new IllegalStateException("Classpath resource is empty: " + resourcePath);
            }
        }
        // Flush OS buffers to durable storage before the atomic rename.
        try (FileChannel channel = FileChannel.open(temp, StandardOpenOption.WRITE)) {
            channel.force(true);
        }
    }

View on GitHub (pinned to cd8cf15dc3)