hasura/graphql-engine · error
can't create directory tree: %w
Error message
can't create directory tree: %w
What it means
While extracting a plugin ZIP, os.MkdirAll failed for a directory entry inside the archive under the target dir. This is a filesystem-level failure: permissions, an existing non-directory file at the path, a read-only target, or disk issues, wrapped under op 'download.extractZIP'.
Source
Thrown at cli/plugins/download/downloader.go:76
func extractZIP(targetDir, fileName string, read io.ReaderAt, size int64) error {
var op errors.Op = "download.extractZIP"
zipReader, err := zip.NewReader(read, size)
if err != nil {
return errors.E(op, err)
}
for _, f := range zipReader.File {
err := suspiciousPath(f.Name)
if err != nil {
return errors.E(op, err)
}
path := filepath.Join(targetDir, filepath.FromSlash(f.Name))
if f.FileInfo().IsDir() {
err := os.MkdirAll(path, f.Mode())
if err != nil {
return errors.E(op, fmt.Errorf("can't create directory tree: %w", err))
}
continue
}
src, err := f.Open()
if err != nil {
return errors.E(op, fmt.Errorf("could not open inflating zip file: %w", err))
}
dst, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, f.Mode())
if err != nil {
_ = src.Close()
return errors.E(op, fmt.Errorf("can't create file in zip destination dir: %w", err))
}
defer func() {View on GitHub (pinned to 724551b9ae)
Solutions
- Check write permissions on targetDir: ls -ld and chmod/chown as needed, or run with appropriate privileges.
- Clean out a partially-extracted plugin directory before re-extracting (remove stale files that conflict with archive directory entries).
- Verify the target filesystem is writable and not full (df, mount options).
- Shorten the plugin install path or enable long-path support on Windows if path length is the issue.
Example fix
// before
os.MkdirAll(path, f.Mode()) // fails: stale file blocks dir
// after
if err := os.RemoveAll(targetDir); err != nil { return err }
os.MkdirAll(path, f.Mode()) Defensive patterns
Strategy: validation
Validate before calling
// Ensure target dir is writable and clear conflicting paths before extract
if info, err := os.Stat(targetDir); err == nil && !info.IsDir() {
return fmt.Errorf("target %s exists and is not a dir", targetDir)
}
if err := os.MkdirAll(targetDir, 0o755); err != nil { return err }
if w, err := fileutil.CanWrite(targetDir); !w { return err } Try / catch
if err := downloader.Get(...); err != nil && strings.Contains(err.Error(), "can't create directory tree") {
os.RemoveAll(targetDir) // clear stale conflicts, retry once
} Prevention
- Extract into a fresh, per-plugin subdirectory
- Clean partial extractions on failure
- Run the CLI as a user owning the plugin directory
When it happens
Trigger: Calling extractZIP when the process lacks write permission on targetDir, when a regular file already exists where the archive expects a directory, when the path is too long, or when the filesystem is full/read-only.
Common situations: Plugin dir owned by root while the CLI runs as a non-root user; a previous partial extraction left files behind; target dir on a read-only mount (container image); path length limits on macOS/Windows.
Related errors
- cannot write metadata to project: %w
- writing metadata to file: %w
- error removing migrations from project: %w
- failed to create seed file: %w
- error getting directory details: %w
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/51280f7345a877e7.
Report an issue: GitHub.