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

  1. Create the store directory before listing: os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm), or simply perform one upload first.
  2. Verify local.store-path in config.yaml is correct and actually exists in the runtime environment.
  3. Fix directory read permissions for the server process user (chmod/chown).
  4. 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

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.