flipped-aurora/gin-vue-admin · error

创建编译根目录失败: %w

Error message

创建编译根目录失败: %w

What it means

compileCliBinary creates the CLI build root directory with os.MkdirAll(absBuildRoot, 0o755). If directory creation fails — permission denied, parent path is a file, read-only filesystem, or disk issues — this wrapped error is returned. The build cannot proceed without a writable build root.

Source

Thrown at server/plugin/ai/service/sys_cli_build.go:89

	applyCliBuildBaseURL(&manifest, req.BaseURL)
	manifestBytes, err := marshalSysCliManifest(manifest)
	if err != nil {
		return "", nil, err
	}
	return s.compileCliBinary(cli, manifestBytes, goos, goarch)
}

// compileCliBinary 把 manifest 内嵌进 gva 源码副本并交叉编译,返回二进制文件名与内容。
func (s *cliService) compileCliBinary(cli autoModel.SysCli, manifestBytes []byte, goos, goarch string) (string, []byte, error) {
	if _, err := exec.LookPath("go"); err != nil {
		return "", nil, fmt.Errorf("服务器未安装 Go 工具链,无法编译: %w", err)
	}
	absBuildRoot, err := filepath.Abs(cliBuildDir)
	if err != nil {
		return "", nil, fmt.Errorf("解析编译目录失败: %w", err)
	}
	if err := os.MkdirAll(absBuildRoot, 0o755); err != nil {
		return "", nil, fmt.Errorf("创建编译根目录失败: %w", err)
	}

	buildDir, err := os.MkdirTemp(absBuildRoot, "cli-")
	if err != nil {
		return "", nil, fmt.Errorf("创建编译目录失败: %w", err)
	}
	defer os.RemoveAll(buildDir)

	if err := copyCliSources(cliSourceDir, buildDir); err != nil {
		return "", nil, err
	}
	if err := writeEmbeddedManifest(buildDir, manifestBytes); err != nil {
		return "", nil, err
	}

	binaryName := sanitizeSingleSegmentSlug(cli.Command)
	if binaryName == "" {
		binaryName = "cli"

View on GitHub (pinned to 3136500ef3)

Solutions

  1. chown/chmod the build root so the server process user can write (e.g. mkdir -p <dir> && chown <user> <dir>)
  2. Mount a writable volume at the build dir in container/K8s deployments
  3. Point cliBuildDir at a guaranteed-writable location (e.g. /tmp or a data volume) via config
  4. Check nothing occupies the path as a regular file (ls -la the parent and remove/rename the file)

Example fix

// before (dockerfile, read-only root)
// no volume for build dir
// after
VOLUME ["/data/gva-cli-build"]
ENV GVA_CLI_BUILD_DIR=/data/gva-cli-build
Defensive patterns

Strategy: validation

Validate before calling

probe := filepath.Join(cliBuildDir, ".write-test")
if err := os.WriteFile(probe, nil, 0o644); err != nil {
    return fmt.Errorf("build dir not writable: %w", err)
}
os.Remove(probe)

Try / catch

bin, content, err := svc.BuildCliBinary(cli, manifest, goos, goarch)
if err != nil {
    if strings.Contains(err.Error(), "创建编译根目录失败") {
        return nil, fmt.Errorf("build root %s not writable: check permissions/read-only FS", cliBuildDir)
    }
    return err
}

Prevention

When it happens

Trigger: Running the server as a non-root user without write permission on the cliBuildDir path; cliBuildDir (or a parent) exists as a regular file; deploying with a read-only root filesystem (e.g. Kubernetes readonlyRootFilesystem or read-only Docker volume).

Common situations: Container hardening with read-only FS and no emptyDir/writable volume mounted at the build dir; directory created previously by root and now the service runs as an unprivileged user; path collision where a file named like the build dir exists.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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