kopia/kopia · error
error opening directory
Error message
error opening directory
What it means
isEmptyDirectory opens the target path with os.Open to test whether it is an empty directory before creating it; if the open itself fails, the error is wrapped as 'error opening directory'. This happens during createDirectory when the restorer needs to know whether an existing directory can be reused.
Solutions
- Grant read (and traverse) permission on the target directory to the user running the restore
- Check the wrapped errno: fix EACCES via chmod/chown, ENAMETOOLONG via shorter paths
- Ensure no concurrent process deletes/moves the target during restore
- Run the restore as a user with sufficient privileges (or with elevated permissions when justified)
Example fix
// before $ kopia restore snap target-dir # run as unprivileged user // after $ sudo chmod a+rx target-dir && kopia restore snap target-dir
Defensive patterns
Strategy: validation
Validate before calling
// Ensure target dir is readable/traversable by current user
fi, err := os.Stat(targetDir)
if err != nil { return err }
if !fi.IsDir() { return fmt.Errorf("%s is not a directory", targetDir) }
if err := syscall.Access(targetDir, unix.R_OK|unix.X_OK); err != nil {
return fmt.Errorf("no read access to %s: %w", targetDir, err)
} Try / catch
if err != nil && strings.Contains(err.Error(), "error opening directory") {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {
// surface permission guidance to user
}
} Prevention
- Run restores with a user that can read all target directories
- Check directory permissions before starting a restore
- Avoid concurrent deletion/moves of the restore target
- Test restore targets with a dry run first
When it happens
Trigger: createDirectory calls isEmptyDirectory(name) and os.Open(name) fails — e.g. permission denied on the path, the path is on an unreadable mount, ENAMETOOLONG, or a race removed the directory between stat and open.
Common situations: Restoring into a directory without read permission, restoring over a path whose permissions changed mid-restore, or running as a non-root user against system-owned directories.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- cannot create directory
- unexpected error creating directory
- can't change file times
- can't create tmp file
- can't get local fs entry
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/9c50b0378499f123.
Report an issue: GitHub.
Appendix: source
Thrown at snapshot/restore/local_fs_output.go:450
Reader: r,
cb: progressCb,
}
log(ctx).Debugf("copying file contents to: %v", targetPath)
targetPath = ospath.SafeLongFilename(targetPath)
if o.WriteFilesAtomically {
//nolint:wrapcheck
return atomicfile.Write(targetPath, rr)
}
return write(targetPath, rr, f.Size(), o.FlushFiles, o.copier)
}
func isEmptyDirectory(name string) (bool, error) {
f, err := os.Open(name) //nolint:gosec
if err != nil {
return false, errors.Wrap(err, "error opening directory")
}
defer f.Close() //nolint:errcheck
if _, err = f.Readdirnames(1); errors.Is(err, io.EOF) {
return true, nil
}
return false, errors.Wrap(err, "error reading directory") // Either not empty or error
}
var _ Output = (*FilesystemOutput)(nil)
View on GitHub (pinned to 82495e54b5)