github/copilot-sdk · error · IllegalStateException
Concurrent extraction race: target already exists but is…
Error message
Concurrent extraction race: target already exists but is not a valid file:
What it means
Thrown by NativeRuntimeLoader's atomic publisher after an ATOMIC_MOVE fails with FileAlreadyExistsException or AccessDeniedException. It means another process/thread won the race to publish the cached native file, but the file already at the target path failed validation (missing, non-regular, or zero bytes). The loader cannot confirm a usable cached copy exists.
Solutions
- Delete the stale cache entry (rm -rf ~/.copilot/runtime-cache) and restart the application so extraction starts from a clean state.
- Retry resolution once — a transient race usually resolves on the second call once the winner's file is valid.
- Serialize startup so only one process performs native extraction (e.g. startup lock or init singleton).
- Point COPILOT_CLI_PATH at a pre-installed runtime to bypass classpath extraction entirely.
Example fix
// before
Path runtime = NativeRuntimeLoader.resolve();
// after
Path runtime;
try {
runtime = NativeRuntimeLoader.resolve();
} catch (IllegalStateException | IOException e) {
Files.walk(Path.of(System.getProperty("user.home"), ".copilot", "runtime-cache"))
.sorted(Comparator.reverseOrder())
.forEach(p -> p.toFile().delete());
runtime = NativeRuntimeLoader.resolve();
} Defensive patterns
Strategy: retry
Validate before calling
Path cache = Path.of(System.getProperty("user.home"), ".copilot", "runtime-cache");
boolean cacheSane = Files.isDirectory(cache) && Files.list(cache).findAny().isPresent();
// if a previous run crashed, clear stale entries before first resolve()
if (!cacheSane) Files.walk(cache).sorted(Comparator.reverseOrder()).forEach(p -> p.toFile().delete()); Try / catch
try {
Path runtime = NativeRuntimeLoader.resolve();
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Concurrent extraction race")) {
deleteRuntimeCache(); // clear bad cache entry
runtime = NativeRuntimeLoader.resolve(); // retry once
} else throw e;
} Prevention
- Avoid multiple JVMs extracting to the shared ~/.copilot/runtime-cache simultaneously; serialize first startup.
- Pre-set COPILOT_CLI_PATH in CI/containers to skip extraction races.
- Clean ~/.copilot/runtime-cache after abnormal JVM termination.
- Keep the cache on a local filesystem, not NFS.
When it happens
Trigger: Two JVMs (or threads) concurrently call NativeRuntimeLoader.resolve()/resolveRuntimeWrapper() on a cold cache; the winner of the atomic move leaves a corrupt, empty, or deleted file at the cache path, and the loser's post-race validity check (isValidCachedFile) fails.
Common situations: Parallel integration-test JVMs sharing ~/.copilot/runtime-cache; multiple app instances in the same container starting simultaneously; a previous crashed run left a zero-byte runtime.node; an antivirus or cleanup process truncated/removed the file between move and validation.
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
- Published runtime wrapper is not a non-empty executable…
- checking existing permissions
- not found at .
- at is empty.
- step(" ") journal returned a hit without a result
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/697bf2c1dba07a80.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java:97
* {@link StandardCopyOption#ATOMIC_MOVE}.
*/
static final AtomicPublisher DEFAULT_PUBLISHER = (temp, cached) -> {
try {
Files.move(temp, cached, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (AtomicMoveNotSupportedException ex) {
throw new IllegalStateException("Filesystem does not support atomic moves; cannot safely publish "
+ RUNTIME_FILENAME + " to " + cached, ex);
} catch (FileAlreadyExistsException | AccessDeniedException ex) {
// Windows can report AccessDeniedException instead of
// FileAlreadyExistsException when another publisher wins the race.
try {
if (isValidCachedFile(cached)) {
return;
}
} catch (IOException ignored) {
// fall through to the error below
}
throw new IllegalStateException(
"Concurrent extraction race: target already exists but is not a valid file: " + cached, ex);
}
};
private NativeRuntimeLoader() {
}
/**
* Resolves the filesystem path to the {@code runtime.node} binary.
*
* <p>
* Follows the three-step resolution order documented on this class. The
* returned path is guaranteed to refer to a regular, non-empty file at the time
* of return.
*
* @return absolute path to the {@code runtime.node} binary
* @throws IOException
* if the binary cannot be located or extractedView on GitHub (pinned to cd8cf15dc3)