{"record":{"id":"30f6ae02d761b66e","repo":"yorukot/superfile","slug":"failed-to-read-source-directory-w","errorCode":null,"errorMessage":"failed to read source directory: %w","messagePattern":"failed to read source directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/internal/file_operations.go","lineNumber":99,"sourceCode":"\t\treturn fmt.Errorf(\"failed to stat source: %w\", err)\n\t}\n\n\tif srcInfo.IsDir() {\n\t\treturn copyDir(src, dst, srcInfo)\n\t}\n\treturn copyFile(src, dst, srcInfo)\n}\n\n// copyDir recursively copies a directory\nfunc copyDir(src, dst string, srcInfo os.FileInfo) error {\n\terr := os.MkdirAll(dst, srcInfo.Mode())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination directory: %w\", err)\n\t}\n\n\tentries, err := os.ReadDir(src)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read source directory: %w\", err)\n\t}\n\n\tfor _, entry := range entries {\n\t\tsrcPath := filepath.Join(src, entry.Name())\n\t\tdstPath := filepath.Join(dst, entry.Name())\n\n\t\tentryInfo, err := entry.Info()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get entry info: %w\", err)\n\t\t}\n\n\t\tif entryInfo.IsDir() {\n\t\t\terr = copyDir(srcPath, dstPath, entryInfo)\n\t\t} else {\n\t\t\terr = copyFile(srcPath, dstPath, entryInfo)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/yorukot/superfile/blob/b72f550bc6e75913f48eb49fdd258e1a2df2fe88/src/internal/file_operations.go#L81-L117","documentation":"After creating the destination directory, copyDir reads the source's entries with os.ReadDir(src); failure is wrapped as 'failed to read source directory'. This means the source directory exists (it was stat'd) but cannot be listed — typically a permissions problem or it being replaced by a non-directory mid-operation.","triggerScenarios":"copyElement -> copyDir where os.ReadDir(src) fails: no read permission on the directory, src is on an inaccessible mount, or src was swapped/removed concurrently after os.Stat.","commonSituations":"Copying a directory with mode 700 owned by another user; reading from a USB/network mount that dropped; racing deletion of the source tree during a paste; encrypted/protected directories.","solutions":["Grant read permission on the source directory (chmod o+r / correct owner).","Reconnect or remount the source volume if it dropped.","Check the source is still a directory (concurrent modification) and retry.","Copy with elevated privileges if system-level protection is intentional."],"exampleFix":"// before\ndrwx------ root root /data/secret   // ReadDir fails as non-root\n// after\nchmod o+rx /data/secret\n// then retry the copy","handlingStrategy":"validation","validationCode":"st, err := os.Stat(src)\nif err != nil { return err }\nif !st.IsDir() { return fmt.Errorf(\"%s is not a directory\", src) }\nprobe, err := os.ReadDir(src) // cheap pre-check of readability\nif err != nil { return fmt.Errorf(\"source not readable: %w\", err) }","typeGuard":"func dirReadable(src string) bool {\n    _, err := os.ReadDir(src)\n    return err == nil\n}","tryCatchPattern":"if err := copyElement(src, dst); err != nil {\n    var pErr *fs.PathError\n    if errors.As(err, &pErr) && errors.Is(pErr.Err, fs.ErrPermission) {\n        log.Errorf(\"Source dir %s unreadable — check permissions/mount\", pErr.Path)\n        return err // or skip-and-report in batch copy\n    }\n    return err\n}","preventionTips":["Verify directory read permission (r bit) before copying trees.","Check mount health (network/USB) before long copy operations.","Skip-and-report unreadable subdirectories instead of aborting the whole copy.","Avoid mutating (deleting/renaming) the source tree while it is being copied."],"tags":["filesystem","copy","directory","permissions"],"backgroundTag":"permission-denied","analyzedSha":"b72f550bc6e75913f48eb49fdd258e1a2df2fe88","analyzedAt":"2026-09-01T04:38:08.254Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}