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
- Grant the running user write access to DataDir: `sudo chown -R <user> <DataDir>` or adjust mode to 0770/0775 with group ownership.
- Set Config.DataDir to a directory the service user owns and can write to.
- Check disk space and inodes: `df -h <DataDir> && df -i <DataDir>`.
- 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
- Ensure the service user can create files in DataDir (test with `touch <DataDir>/.probe`).
- Under systemd hardening (ProtectSystem=strict), add DataDir to ReadWritePaths=.
- Monitor free space and inodes on the DataDir filesystem.
- Keep DataDir off read-only or noexec-constrained mounts.
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
- failed to rename the current executable: %w
- failed replacing the executable: %w
- data directory not found
- chmod: %w
- failed to open release for checksum verification: %w
AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31).
Data as JSON: /api/errors/9c6e6c417dcd7b95.
Report an issue: GitHub.