flipped-aurora/gin-vue-admin · error
function os.ReadDir() failed, err:
Error message
function os.ReadDir() failed, err:
What it means
This error is returned by Local.ListFiles when os.ReadDir on the configured store path (global.GVA_CONFIG.Local.StorePath) fails. ReadDir fails when the directory does not exist, the process lacks read permission, or the path points to a file rather than a directory. The OS error is appended to the message.
Source
Thrown at server/utils/upload/local.go:164
for _, key := range keys {
if err := l.DeleteFile(ctx, key); err != nil {
logger.WithCtx(ctx).Mod("upload").Err(err).Error("function Local.DeleteFile() failed")
failed = append(failed, DeleteFailure{Key: key, Err: err})
}
}
return failed, nil
}
// ListFiles 按前缀列举本地文件,cursor 为上次返回的最后一条文件名(不透明游标)。
func (*Local) ListFiles(ctx context.Context, prefix, cursor string, limit int) ([]FileInfo, string, bool, error) {
if limit <= 0 {
limit = 100
}
entries, err := os.ReadDir(global.GVA_CONFIG.Local.StorePath)
if err != nil {
logger.WithCtx(ctx).Mod("upload").Err(err).Error("function os.ReadDir() failed")
return nil, "", false, errors.New("function os.ReadDir() failed, err:" + err.Error())
}
// 仅保留普通文件,按文件名过滤
names := make([]string, 0, len(entries))
nameToEntry := make(map[string]os.DirEntry, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if prefix != "" && !strings.HasPrefix(name, prefix) {
continue
}
names = append(names, name)
nameToEntry[name] = entry
}
// 按文件名排序,按 cursor 之后开始分页View on GitHub (pinned to 3136500ef3)
Solutions
- Create the store directory before listing: os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm), or simply perform one upload first.
- Verify local.store-path in config.yaml is correct and actually exists in the runtime environment.
- Fix directory read permissions for the server process user (chmod/chown).
- Check the appended OS error: 'no such file or directory' means create it; 'permission denied' means fix permissions.
Example fix
// before files, _, _, err := local.ListFiles(ctx, "", "", 20) // store dir never created // after _ = os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm) files, _, _, err := local.ListFiles(ctx, "", "", 20)
Defensive patterns
Strategy: validation
Validate before calling
func ensureStoreDir(storePath string) error {
fi, err := os.Stat(storePath)
if os.IsNotExist(err) {
return os.MkdirAll(storePath, 0o755)
}
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%s is not a directory", storePath)
}
return nil
}
// call before ListFiles: ensureStoreDir(global.GVA_CONFIG.Local.StorePath) Try / catch
files, cursor, more, err := local.ListFiles(ctx, prefix, cursor, limit)
if err != nil {
if strings.HasPrefix(err.Error(), "function os.ReadDir() failed") {
_ = os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm)
files, cursor, more, err = local.ListFiles(ctx, prefix, cursor, limit)
}
if err != nil {
return err
}
} Prevention
- Create the store directory during deployment/bootstrap, not on first upload.
- Validate store-path exists and is a readable directory in a startup check.
- Double-check config.yaml spelling of local.store-path per environment.
- Ensure container volumes are mounted before the app starts serving requests.
When it happens
Trigger: Calling ListFiles before any upload has ever created the store directory (MkdirAll only runs in UploadFile), pointing local.store-path at a nonexistent directory, or running with a user that cannot read the directory.
Common situations: Fresh deployments where nobody has uploaded yet; config typo in store-path; container volume not mounted so the path doesn't exist; permission changes after a security hardening pass.
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/a9c2871ab9141753.
Report an issue: GitHub.