henrygd/beszel · error

failed to create update directory: %w

Error message

failed to create update directory: %w

What it means

After ensuring DataDir exists, update() creates a unique temporary staging directory inside it with os.MkdirTemp. Failure here (wrapped with this message) usually means DataDir exists but is not writable by the current user, or a race/permission quirk prevents creating new entries in it.

Source

Thrown at internal/ghupdate/ghupdate.go:147

	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...")
	if err := verifyAssetChecksum(assetPath, asset.Digest); err != nil {
		return false, err
	}

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Grant the running user write access to DataDir: `sudo chown -R <user> <DataDir>` or adjust mode to 0770/0775 with group ownership.
  2. Set Config.DataDir to a directory the service user owns and can write to.
  3. Check disk space and inodes: `df -h <DataDir> && df -i <DataDir>`.
  4. If running under systemd hardening, add DataDir to ReadWritePaths= in the unit file.

Example fix

// before (systemd unit)
[Service]
ProtectSystem=strict
// after
[Service]
ProtectSystem=strict
ReadWritePaths=/var/lib/beszel
Defensive patterns

Strategy: validation

Validate before calling

probe, err := os.CreateTemp(cfg.DataDir, ".write-probe-*")
if err != nil {
    return fmt.Errorf("DataDir %q is not writable: %w", cfg.DataDir, err)
}
probe.Close()
os.Remove(probe.Name())

Try / catch

updated, err := ghupdate.Update(cfg)
if err != nil && strings.Contains(err.Error(), "failed to create update directory") {
    log.Printf("cannot create staging dir in DataDir (%v); check write permission, disk space, and systemd ReadWritePaths", err)
}

Prevention

When it happens

Trigger: ghupdate.Update -> update when os.MkdirTemp(DataDir, ".beszel_update-") fails: DataDir has 0755 owned by another user, noexec/no-write mount options, disk full (inode or space exhaustion), or a security module blocking temp dir creation.

Common situations: DataDir writable only by root while the updater runs unprivileged; container with read-only rootfs and DataDir defaulting to os.TempDir(); tmpfs full; paranoid umask or hardened systemd service settings (ProtectSystem=strict without ReadWritePaths).

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/9c6e6c417dcd7b95. Report an issue: GitHub.