flipped-aurora/gin-vue-admin · error

保存技能包失败: %w

Error message

保存技能包失败: %w

What it means

This error wraps a failure to persist the downloaded online skill package (zip) to a temporary file on disk during DownloadOnlineSkill. The io.Copy call streams the HTTP response body into tmpFile; any read error from the network stream or write error to the local filesystem is wrapped with this message. It is a disk/IO failure, not a validation failure.

Source

Thrown at server/service/system/sys_skills.go:456

	if err != nil {
		return fmt.Errorf("下载压缩包失败: %w", err)
	}
	defer zipResp.Body.Close()

	if zipResp.StatusCode != http.StatusOK {
		return fmt.Errorf("下载压缩包失败, HTTP状态码: %d", zipResp.StatusCode)
	}

	tmpFile, err := os.CreateTemp("", "gva-skill-*.zip")
	if err != nil {
		return fmt.Errorf("创建临时文件失败: %w", err)
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)

	if _, err = io.Copy(tmpFile, zipResp.Body); err != nil {
		tmpFile.Close()
		return fmt.Errorf("保存技能包失败: %w", err)
	}
	tmpFile.Close()

	if err = extractZipToDir(tmpPath, skillsDir); err != nil {
		return fmt.Errorf("解压技能包失败: %w", err)
	}

	return nil
}

func extractZipToDir(zipPath, destDir string) error {
	r, err := zip.OpenReader(zipPath)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Check free disk space on the partition backing os.TempDir (or TMPDIR) and clean up if full
  2. Verify network connectivity/proxy stability to the skill download host and retry the download
  3. Ensure the process has write permission to the temp directory in your deployment environment
  4. Inspect the wrapped cause (%w) in logs to distinguish network read errors from file write errors

Example fix

// before
if _, err = io.Copy(tmpFile, zipResp.Body); err != nil {
    tmpFile.Close()
    return fmt.Errorf("保存技能包失败: %w", err)
}
// after
if _, err = io.Copy(tmpFile, zipResp.Body); err != nil {
    tmpFile.Close()
    if netErr, ok := err.(net.Error); ok {
        return fmt.Errorf("下载网络中断,请重试: %w", netErr)
    }
    return fmt.Errorf("保存技能包失败: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: preflight checks before download
if st, err := os.Stat(os.TempDir()); err != nil || !st.IsDir() {
    return fmt.Errorf("临时目录不可用")
}
if avail := diskFree(os.TempDir()); avail < minNeededBytes {
    return fmt.Errorf("临时目录空间不足")
}

Type guard

// Go: classify the wrapped error
func isNetErr(err error) bool {
    var ne net.Error
    return errors.As(err, &ne)
}
func isPathErr(err error) bool {
    var pe *fs.PathError
    return errors.As(err, &pe)
}

Try / catch

err := svc.DownloadOnlineSkill(name)
if err != nil {
    var pathErr *fs.PathError
    if errors.As(err, &pathErr) && os.IsPermission(pathErr) {
        // fix temp-dir permissions and retry
    } else if errors.Is(err, syscall.ENOSPC) {
        // free disk space and retry
    }
    log.Errorf("下载技能包失败: %v", err)
}

Prevention

When it happens

Trigger: Calling DownloadOnlineSkill when the HTTP response body of the skill package download cannot be fully read (connection dropped mid-transfer) or when writing to the temp file fails (disk full, permission denied on os.TempDir, tmpFile already closed).

Common situations: Unstable network/proxy interrupting the download of the skill zip; insufficient disk space in the temp directory; restricted tmp permissions in containerized deployments; read-only filesystem after a disk remount.

Related errors


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