github/copilot-sdk · error · IOException
Unsafe runtime asset inventory path:
Error message
Unsafe runtime asset inventory path:
What it means
NativeRuntimeLoader extracts native runtime assets listed in an inventory file into a local cache directory. Each inventory line is '<octal-mode> <path>'. This error is thrown when the path part of an inventory entry is absolute or, after normalization, starts with '..' — i.e. it does not stay relative to the cache directory. The library rejects such entries defensively to prevent path-traversal style escapes before anything is written to disk.
Solutions
- Verify the integrity of the jar/classpath artifact containing the runtime asset inventory (checksum, re-download from official source).
- Inspect the inventory resource 'native/<classifier>/.../inventory' and fix any entry so paths are plain relative paths with no '..' segments.
- Ensure no custom ClassLoader is injecting an alternate inventory resource that shadows the bundled one.
Example fix
// before (inventory line) 0 sept 755 ../../evil/node // after 0 sept 755 bin/node
Defensive patterns
Strategy: validation
Validate before calling
// Java: nothing to pre-validate as a caller (inventory is internal), but you can pre-scan the artifact
URL inv = loader.getResource(inventoryResourcePath);
try (BufferedReader r = new BufferedReader(new InputStreamReader(inv.openStream(), StandardCharsets.UTF_8))) {
for (String line; (line = r.readLine()) != null; ) {
String p = line.substring(line.indexOf(' ') + 1).trim();
if (Path.of(p).isAbsolute() || Path.of(p).normalize().startsWith(".."))
throw new IllegalStateException("Unsafe inventory entry: " + p);
}
} Try / catch
try { loader.loadRuntime(); } catch (IOException e) { if (e.getMessage().startsWith("Unsafe runtime asset inventory path")) { /* replace artifact jar; do not retry */ } else throw e; } Prevention
- Verify artifact checksums/signatures before putting native jars on the classpath
- Never hand-edit inventory resources; regenerate them with the official tooling
- Pin artifact versions from trusted repositories only
When it happens
Trigger: extractRuntimeAssetsToCache parses an inventory resource whose line's path field begins with '/', a drive letter, or contains '..' segments that survive normalization (e.g. '../../etc/passwd', '/usr/bin/node').
Common situations: A tampered or corrupted runtime asset inventory inside a repackaged/copilot-native jar; a hand-edited or generated inventory file with wrong path format; a malicious supply-chain artifact substituting the 'native/<classifier>/...' inventory.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Runtime asset escapes cache directory:
- Runtime asset not found on classpath:
- Published Copilot CLI is not a non-empty executable file:
- Unsafe runtime package path
- unsafe runtime asset path
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/45ed2f0ba46f4960.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java:461
if (inventoryResource == null) {
return;
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inventoryResource.openStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.isBlank()) {
continue;
}
String[] fields = line.split("\\t", 2);
if (fields.length != 2) {
throw new IOException("Invalid runtime asset inventory entry: " + line);
}
boolean executable = (Integer.parseInt(fields[0], 8) & 0111) != 0;
Path relative = Path.of(fields[1]).normalize();
if (relative.isAbsolute() || relative.startsWith("..")) {
throw new IOException("Unsafe runtime asset inventory path: " + fields[1]);
}
Path cached = cacheDir.resolve(relative).normalize();
if (!cached.startsWith(cacheDir)) {
throw new IOException("Runtime asset escapes cache directory: " + fields[1]);
}
if (isValidCachedFile(cached) && (!executable || isWindows() || Files.isExecutable(cached))) {
continue;
}
String resourcePath = "native/" + classifier + "/" + fields[1];
URL resource = loader.getResource(resourcePath);
if (resource == null) {
throw new FileNotFoundException("Runtime asset not found on classpath: " + resourcePath);
}
Files.createDirectories(cached.getParent());
Path temp = Files.createTempFile(cached.getParent(), "runtime-asset-tmp-", "");
try {
copyResourceToTemp(resource, resourcePath, temp);View on GitHub (pinned to cd8cf15dc3)