{"record":{"id":"9c50b0378499f123","repo":"kopia/kopia","slug":"error-opening-directory","errorCode":null,"errorMessage":"error opening directory","messagePattern":"error opening directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"snapshot/restore/local_fs_output.go","lineNumber":450,"sourceCode":"\t\tReader: r,\n\t\tcb:     progressCb,\n\t}\n\n\tlog(ctx).Debugf(\"copying file contents to: %v\", targetPath)\n\ttargetPath = ospath.SafeLongFilename(targetPath)\n\n\tif o.WriteFilesAtomically {\n\t\t//nolint:wrapcheck\n\t\treturn atomicfile.Write(targetPath, rr)\n\t}\n\n\treturn write(targetPath, rr, f.Size(), o.FlushFiles, o.copier)\n}\n\nfunc isEmptyDirectory(name string) (bool, error) {\n\tf, err := os.Open(name) //nolint:gosec\n\tif err != nil {\n\t\treturn false, errors.Wrap(err, \"error opening directory\")\n\t}\n\n\tdefer f.Close() //nolint:errcheck\n\n\tif _, err = f.Readdirnames(1); errors.Is(err, io.EOF) {\n\t\treturn true, nil\n\t}\n\n\treturn false, errors.Wrap(err, \"error reading directory\") // Either not empty or error\n}\n\nvar _ Output = (*FilesystemOutput)(nil)\n","sourceCodeStart":432,"sourceCodeEnd":463,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/snapshot/restore/local_fs_output.go#L432-L463","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\n$ kopia restore snap target-dir  # run as unprivileged user\n// after\n$ sudo chmod a+rx target-dir && kopia restore snap target-dir","handlingStrategy":"validation","validationCode":"// Ensure target dir is readable/traversable by current user\nfi, err := os.Stat(targetDir)\nif err != nil { return err }\nif !fi.IsDir() { return fmt.Errorf(\"%s is not a directory\", targetDir) }\nif err := syscall.Access(targetDir, unix.R_OK|unix.X_OK); err != nil {\n\treturn fmt.Errorf(\"no read access to %s: %w\", targetDir, err)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"error opening directory\") {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {\n\t\t// surface permission guidance to user\n\t}\n}","preventionTips":["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"],"tags":["filesystem","directory","permissions","open"],"backgroundTag":"permission-denied","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}