henrygd/beszel · error

failed to create update data directory: %w

Error message

failed to create update data directory: %w

What it means

Before staging the download, update() creates Config.DataDir (defaulting to os.TempDir()) recursively with os.MkdirAll. If that fails — permission denied, read-only filesystem, path is a file, etc. — the error is wrapped with this message. No download happens until a writable data directory exists.

Source

Thrown at internal/ghupdate/ghupdate.go:143

		return false, err
	}

	currentVersion := semver.MustParse(strings.TrimPrefix(p.currentVersion, "v"))
	newVersion := semver.MustParse(strings.TrimPrefix(latest.Tag, "v"))

	if newVersion.LTE(currentVersion) {
		ColorPrintf(ColorGreen, "You already have the latest version %s.", p.currentVersion)
		return false, nil
	}

	suffix := archiveSuffix(p.config.ArchiveExecutable, runtime.GOOS, runtime.GOARCH, buildGOARM)
	asset, err := latest.findAssetBySuffix(suffix)
	if err != nil {
		return false, err
	}

	if err := os.MkdirAll(p.config.DataDir, 0755); err != nil {
		return false, fmt.Errorf("failed to create update data directory: %w", err)
	}
	releaseDir, err := os.MkdirTemp(p.config.DataDir, ".beszel_update-")
	if err != nil {
		return false, fmt.Errorf("failed to create update directory: %w", err)
	}
	defer os.RemoveAll(releaseDir)

	ColorPrintf(ColorYellow, "Downloading %s...", asset.Name)

	// download the release asset
	assetPath, err := archivePath(releaseDir, asset.Name)
	if err != nil {
		return false, err
	}
	if err := downloadFile(p.config.Context, p.config.HttpClient, asset.DownloadUrl, assetPath, p.config.UseMirror); err != nil {
		return false, err
	}
	ColorPrint(ColorYellow, "Verifying checksum...")

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Create the directory manually with correct ownership: `sudo mkdir -p <DataDir> && sudo chown <user> <DataDir>`.
  2. Point Config.DataDir at a writable location for the user running the process (e.g. /var/lib/beszel owned by the service user).
  3. Check that the path isn't an existing regular file and the filesystem isn't mounted read-only (`mount | grep <path>`).
  4. Review SELinux/AppArmor audit logs if permissions look correct but creation still fails.

Example fix

// before
Update(ghupdate.Config{DataDir: "/root/.beszel"}) // running as non-root
// after
Update(ghupdate.Config{DataDir: "/home/deploy/.beszel"})
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(cfg.DataDir, 0755); err != nil {
    return fmt.Errorf("pre-flight: cannot create DataDir %q: %w", cfg.DataDir, err)
}
if fi, err := os.Stat(cfg.DataDir); err != nil || !fi.IsDir() {
    return errors.New("DataDir path exists but is not a directory")
}

Try / catch

updated, err := ghupdate.Update(cfg)
if err != nil && strings.Contains(err.Error(), "failed to create update data directory") {
    log.Printf("DataDir %q is not creatable (%v); fix ownership/mount or choose a writable path", cfg.DataDir, err)
}

Prevention

When it happens

Trigger: ghupdate.Update -> update when os.MkdirAll(DataDir, 0755) fails: DataDir points at a path the running user cannot create, an existing file occupies the path, or the filesystem is read-only.

Common situations: Running the agent as an unprivileged user while DataDir is owned by root; DataDir set to a path inside a read-only container layer; a regular file accidentally exists at the DataDir path; SELinux/AppArmor denial.

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 henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/2319f2ea6e77965e. Report an issue: GitHub.