github/copilot-sdk · error
unsafe runtime asset path
Error message
unsafe runtime asset path %q
What it means
For each regular file in the tar, installRuntimeAssets cleans the entry name and requires filepath.IsLocal(clean) before writing. Non-local names (absolute paths, '..' escapes) are rejected with "unsafe runtime asset path" to block path traversal / zip-slip extraction outside installDir.
Solutions
- Do not disable this check — the archive is untrusted or malformed
- Repackage the runtime archive so all member names are relative and within the archive root
- Inspect the archive (tar -tvf) to find the offending member and fix the packaging script
- Only bypass via a vetted, trusted archive after security review
Example fix
// before // packaging script: // tar -czf runtime.tar.gz /abs/path/assets // absolute members -> rejected // after // (cd /abs/path && tar -czf runtime.tar.gz assets) // relative members only
Defensive patterns
Strategy: validation
Validate before calling
archive, _ := io.ReadAll(src)
_ = iterateTarHeaders(archive, func(name string) error {
clean := filepath.Clean(filepath.FromSlash(name))
if !filepath.IsLocal(clean) {
return fmt.Errorf("archive contains unsafe member %q", name)
}
return nil
}) Type guard
func safeArchiveMember(name string) bool {
return filepath.IsLocal(filepath.Clean(filepath.FromSlash(name)))
} Try / catch
err := embeddedcli.InstallRuntime(ctx, cfg, dir)
if err != nil && strings.Contains(err.Error(), "unsafe runtime asset path") {
return fmt.Errorf("refusing to install: runtime archive contains path-traversal members; repackage it")
} Prevention
- Package archives with relative member names only
- Scan third-party archives for absolute/.. members before distributing
- Never attempt to bypass the IsLocal check — it blocks zip-slip attacks
When it happens
Trigger: A tar entry header.Name is absolute ("/etc/passwd"), contains ".." ("../../x"), or otherwise resolves outside the install directory after filepath.Clean.
Common situations: A maliciously crafted or wrongly packaged runtime archive containing absolute or parent-relative paths; archives built on systems embedding absolute member names.
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
- Unsafe runtime asset inventory path:
- Runtime asset escapes cache directory:
- Unsafe runtime package path
- Checksum mismatch for
- Unsupported runtime package entry
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/47d91c4279da90e4.
Report an issue: GitHub.
Appendix: source
Thrown at go/internal/embeddedcli/embeddedcli.go:397
if err != nil {
return fmt.Errorf("opening runtime assets: %w", err)
}
defer gzipReader.Close()
tarReader := tar.NewReader(gzipReader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("reading runtime assets: %w", err)
}
if header.Typeflag != tar.TypeReg {
continue
}
clean := filepath.Clean(filepath.FromSlash(header.Name))
if !filepath.IsLocal(clean) {
return fmt.Errorf("unsafe runtime asset path %q", header.Name)
}
content, err := io.ReadAll(tarReader)
if err != nil {
return fmt.Errorf("reading runtime asset %q: %w", header.Name, err)
}
path := filepath.Join(installDir, clean)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("creating runtime asset directory: %w", err)
}
hash := sha256.Sum256(content)
mode := os.FileMode(header.Mode & 0777)
if err := installVerifiedFile(path, bytes.NewReader(content), hash[:], mode, "runtime asset"); err != nil {
return err
}
}
runtimeAssetsInstalled = true
return nil
}View on GitHub (pinned to cd8cf15dc3)