github/copilot-sdk · error · IOException

Published runtime wrapper is not a non-empty executable…

Error message

Published runtime wrapper is not a non-empty executable file: 

What it means

After copying the runtime wrapper to a temp file and atomically publishing it into the cache, the loader re-validates the published file (non-empty, regular, executable) and it fails. This indicates the publish produced a corrupt or unusable file rather than a clean extraction.

Solutions

  1. Clear the runtime cache (rm -rf ~/.copilot/runtime-cache) and retry resolution.
  2. Ensure the cache directory is on a local filesystem that supports atomic moves and POSIX permissions (avoid NFS homes).
  3. Run only one instance at a time or serialize first startup; then restart to re-extract.
  4. Check that no antivirus/cleanup daemon is deleting or chmod'ing files under ~/.copilot/runtime-cache.

Example fix

// before
Path wrapper = NativeRuntimeLoader.resolveRuntimeWrapper();

// after: validate cache and clear it if the wrapper is unusable
Path wrapper;
try {
    wrapper = NativeRuntimeLoader.resolveRuntimeWrapper();
} catch (IOException e) {
    Files.walkFileTree(Path.of(System.getProperty("user.home"), ".copilot", "runtime-cache"),
        new SimpleFileVisitor<>() { public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException { Files.delete(f); return FileVisitResult.CONTINUE; } });
    wrapper = NativeRuntimeLoader.resolveRuntimeWrapper();
}
Defensive patterns

Strategy: retry

Validate before calling

Path wrapper = Path.of(System.getProperty("user.home"), ".copilot", "runtime-cache")
    // walk to wrapper once resolved; precheck: existing wrapper must be non-empty and executable
boolean ok = Files.isRegularFile(p) && Files.size(p) > 0 && (isWindows() || Files.isExecutable(p));

Try / catch

try {
    Path wrapper = NativeRuntimeLoader.resolveRuntimeWrapper();
} catch (IOException e) {
    if (e.getMessage().startsWith("Published runtime wrapper")) {
        deleteRuntimeCache();
        wrapper = NativeRuntimeLoader.resolveRuntimeWrapper();
    } else throw e;
}

Prevention

When it happens

Trigger: A concurrent publisher replaced cachedWrapper with a truncated/empty file between publish and validation; the cache directory filesystem doesn't preserve the executable bit; disk-full or permission issue corrupted the moved file.

Common situations: Shared NFS/network home directories without reliable atomic move semantics; concurrent app instances racing on ~/.copilot/runtime-cache; security software stripping the execute bit after extraction; read-only or quota-exceeded cache directory.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

        }

        String resourcePath = "native/" + classifier + "/" + wrapperName;
        URL resource = loader.getResource(resourcePath);
        if (resource == null) {
            throw new FileNotFoundException("Runtime wrapper not found on classpath: " + resourcePath
                    + " — add the matching classifier JAR to the classpath");
        }

        Path temp = Files.createTempFile(cacheDir, "runtime-wrapper-tmp-", "");
        try {
            copyResourceToTemp(resource, resourcePath, temp);
            makeExecutable(temp);
            DEFAULT_PUBLISHER.publish(temp, cachedWrapper);
        } finally {
            tryDelete(temp);
        }
        if (!isValidCachedCli(cachedWrapper)) {
            throw new IOException("Published runtime wrapper is not a non-empty executable file: " + cachedWrapper);
        }
        return cachedWrapper;
    }

    static Path resolveEntrypoint(String configuredCli, Path runtimePath) throws IOException {
        if (configuredCli != null && !configuredCli.isBlank()) {
            Path configuredPath = Path.of(configuredCli).toAbsolutePath().normalize();
            if (resolveFromCliPath(configuredCli) != null && Files.isRegularFile(configuredPath)
                    && Files.size(configuredPath) > 0) {
                return configuredPath;
            }
        }

        Path parent = runtimePath.getParent();
        String cliName = isWindows() ? CLI_FILENAME_WINDOWS : CLI_FILENAME;
        Path cliPath = parent.resolve(cliName);
        if (Files.isRegularFile(cliPath) && Files.size(cliPath) > 0) {
            return cliPath;

View on GitHub (pinned to cd8cf15dc3)