Billionmail/BillionMail · error

failed to create temporary directory %s: %v

Error message

failed to create temporary directory %s: %v

What it means

updateDockerCompose needs the container-side data directory (../core/data) to stage temp copies of docker-compose.yml via the host volume mapping. If the directory does not exist and gfile.Mkdir cannot create it, this error aborts the compose update before any Docker command runs. It indicates a filesystem permission or path problem inside the container.

Source

Thrown at core/internal/service/multi_ip_domain/config_manager.go:171

}

// updateDockerCompose Generate docker-compose_addnetwork.yml
func (m *ConfigManager) updateDockerCompose(ctx context.Context, configs []map[string]interface{}) error {
	originalPath := filepath.Join(public.HostWorkDir, "docker-compose.yml")
	outputPath := filepath.Join(public.HostWorkDir, "docker-compose_addnetwork.yml")

	// 设置临时文件路径
	// 容器内路径:/opt/billionmail/core/data (通过 public.AbsPath 获取)
	// 宿主机路径:./core-data (相对于 docker-compose.yml 所在目录)
	containerDataPath := public.AbsPath("../core/data/")
	hostDataPath := filepath.Join(public.HostWorkDir, "core-data") // 宿主机的实际映射路径
	tempDockerComposePath := filepath.Join(containerDataPath, "temp_docker-compose.yml")
	hostTempDockerComposePath := filepath.Join(hostDataPath, "temp_docker-compose.yml")

	// Ensure container directory exists
	if !gfile.Exists(containerDataPath) {
		if err := gfile.Mkdir(containerDataPath); err != nil {
			return fmt.Errorf("failed to create temporary directory %s: %v", containerDataPath, err)
		}
	}

	dk, err := docker.NewDockerAPI()
	if err != nil {
		return gerror.New(public.LangCtx(ctx, "failed to create Docker API instance: %v", err.Error()))
	}
	defer dk.Close()

	// Step 1: 复制宿主机的 docker-compose.yml 到宿主机的 core-data 目录
	// 注意:这里目标路径使用宿主机的路径,因为 chroot /host_root 看到的是宿主机文件系
	copyCmd := []string{
		"/bin/sh", "-c",
		fmt.Sprintf(`chroot /host_root cp "%s" "%s"`, originalPath, hostTempDockerComposePath),
	}
	result, err := dk.ExecHostCommand(ctx, copyCmd)
	if err != nil || result.ExitCode != 0 {
		return gerror.New("failed to copy original configuration file, please check path and permissions")

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Create the directory manually with correct ownership: mkdir -p <containerDataPath> && chown <process-user> <containerDataPath>.
  2. Use gfile.Mkdir (recursive) instead of single-level Mkdir so a missing parent does not abort.
  3. Verify the volume mount for core/data exists in docker-compose.yml and is writable.
  4. Confirm the process work directory is correct so AbsPath resolves the intended location.

Example fix

// before
if !gfile.Exists(containerDataPath) {
	if err := gfile.Mkdir(containerDataPath); err != nil {
		return fmt.Errorf("failed to create temporary directory %s: %v", containerDataPath, err)
	}
}
// after
if !gfile.Exists(containerDataPath) {
	if err := gfile.Mkdir(containerDataPath); err != nil {
		return fmt.Errorf("failed to create temporary directory %s: %v", containerDataPath, err)
	}
}
Defensive patterns

Strategy: validation

Validate before calling

dir := public.AbsPath("../core/data")
if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return fmt.Errorf("cannot prepare temp dir %s: %v", dir, err)
	}
}

Try / catch

if err := mgr.ApplyConfigsWithRollback(ctx, configs); err != nil {
	if strings.Contains(err.Error(), "failed to create temporary directory") {
		// mkdir the path manually with correct ownership, then retry
	}
}

Prevention

When it happens

Trigger: gfile.Exists(containerDataPath) is false and gfile.Mkdir fails: parent dir missing, no write permission, read-only filesystem, or an invalid path component.

Common situations: Deployment where the core/data volume was pruned and the parent directory is root-owned; image runs as non-root; working directory moved so public.AbsPath('../core/data') resolves somewhere unwritable.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/ff8d47a1ba28b689. Report an issue: GitHub.