flipped-aurora/gin-vue-admin · error

function os.MkdirAll() failed, err:

Error message

function os.MkdirAll() failed, err:

What it means

local.UploadFile creates the configured store directory with os.MkdirAll before writing the uploaded file. If the OS refuses (permission denied, path is a file, read-only filesystem), the mkdirErr is wrapped in this message and the upload aborts. This is purely a filesystem/permissions problem, not a network one.

Source

Thrown at server/utils/upload/local.go:45

//@object: *Local
//@function: UploadFile
//@description: 上传文件
//@param: file *multipart.FileHeader
//@return: string, string, error

func (*Local) UploadFile(ctx context.Context, file *multipart.FileHeader) (string, string, error) {
	// 读取文件后缀
	ext := filepath.Ext(file.Filename)
	// 读取文件名并加密
	name := strings.TrimSuffix(file.Filename, ext)
	name = utils.MD5V([]byte(name))
	// 拼接新文件名
	filename := name + "_" + time.Now().Format("20060102150405") + ext
	// 尝试创建此路径
	mkdirErr := os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm)
	if mkdirErr != nil {
		logger.WithCtx(ctx).Mod("upload").Err(mkdirErr).Error("function os.MkdirAll() failed")
		return "", "", errors.New("function os.MkdirAll() failed, err:" + mkdirErr.Error())
	}
	// 拼接路径和文件名
	p := global.GVA_CONFIG.Local.StorePath + "/" + filename
	filepath := global.GVA_CONFIG.Local.Path + "/" + filename

	f, openError := file.Open() // 读取文件
	if openError != nil {
		logger.WithCtx(ctx).Mod("upload").Err(openError).Error("function file.Open() failed")
		return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
	}
	defer f.Close() // 创建文件 defer 关闭

	out, createErr := os.Create(p)
	if createErr != nil {
		logger.WithCtx(ctx).Mod("upload").Err(createErr).Error("function os.Create() failed")

		return "", "", errors.New("function os.Create() failed, err:" + createErr.Error())
	}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Check the wrapped err text: 'permission denied' -> chown/chmod the StorePath parent for the server's user
  2. Confirm the path in config Local.StorePath is correct and not colliding with an existing file
  3. If running in Docker/K8s, ensure the volume is mounted read-write
  4. On systemd, review hardening options (ProtectSystem, ReadOnlyPaths) or add ReadWritePaths for the store path
  5. Check disk space and mount status (df -h, mount) for read-only remounts after errors

Example fix

// before
store-path: /var/lib/gva/uploads   # owned by root, server runs as gva
// after
# sudo mkdir -p /var/lib/gva/uploads && sudo chown gva:gva /var/lib/gva/uploads
Defensive patterns

Strategy: validation

Validate before calling

storePath := global.GVA_CONFIG.Local.StorePath
if fi, err := os.Stat(storePath); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s exists and is not a directory", storePath)
}
if err := os.MkdirAll(storePath, os.ModePerm); err != nil {
    return fmt.Errorf("store path not writable: %w", err)
}

Type guard

null

Try / catch

url, name, err := uploader.UploadFile(ctx, fileHeader, fileName)
if err != nil {
    if strings.Contains(err.Error(), "os.MkdirAll() failed") {
        logger.Error("store path unusable — check permissions/mount", zap.Error(err))
        return c.JSON(500, gin.H{"msg": "storage path unavailable"})
    }
    return err
}

Prevention

When it happens

Trigger: UploadFile(ctx, file, ...) when global.GVA_CONFIG.Local.StorePath cannot be created: parent directory lacks write permission, a regular file exists at that path, the disk is read-only or full, or the path is invalid for the OS.

Common situations: Docker volume mounted read-only, StorePath configured under /root without privileges, SELinux/AppArmor denials, systemd service with restrictive ProtectSystem/ReadOnlyPaths, config pointing at a path where a file (e.g. a bind-mounted file) already exists.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/0eb6966e5a46cb56. Report an issue: GitHub.