henrygd/beszel · error

failed to create smartctl directory: %w

Error message

failed to create smartctl directory: %w

What it means

Thrown by ensureEmbeddedSmartctl in agent/smart_windows.go when os.MkdirAll fails to create the temp extraction directory %TEMP%\\beszel\\smartmontools where the bundled smartctl.exe is written at runtime. It wraps the underlying OS error (e.g. permission denied, disk full, read-only volume). Until this succeeds, no embedded smartctl path is available and SMART data collection is skipped.

Source

Thrown at agent/smart_windows.go:26

	"os"
	"path/filepath"
	"sync"
)

//go:embed smartmontools/smartctl.exe
var embeddedSmartctl []byte

var (
	smartctlOnce sync.Once
	smartctlPath string
	smartctlErr  error
)

func ensureEmbeddedSmartctl() (string, error) {
	smartctlOnce.Do(func() {
		destDir := filepath.Join(os.TempDir(), "beszel", "smartmontools")
		if err := os.MkdirAll(destDir, 0o755); err != nil {
			smartctlErr = fmt.Errorf("failed to create smartctl directory: %w", err)
			return
		}

		destPath := filepath.Join(destDir, "smartctl.exe")
		if err := os.WriteFile(destPath, embeddedSmartctl, 0o755); err != nil {
			smartctlErr = fmt.Errorf("failed to write embedded smartctl: %w", err)
			return
		}

		smartctlPath = destPath
	})

	return smartctlPath, smartctlErr
}

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Verify TMP/TEMP point to a writable directory and that the user can create folders there
  2. Check disk free space and file-system permissions on the temp volume
  3. Exclude %TEMP%\\beszel from antivirus/EDR write blocking or relocate temp via TMP env var for the agent process
  4. Check the wrapped %w cause: it names the exact OS reason (permission, path not found, etc.)

Example fix

// before
if err := os.MkdirAll(destDir, 0o755); err != nil {
	return fmt.Errorf("failed to create smartctl directory: %w", err)
}
// after
if err := os.MkdirAll(destDir, 0o755); err != nil {
	log.Warn().Err(err).Str("dir", destDir).Msg("smartctl dir creation failed; SMART disabled")
	return nil // degrade gracefully instead of failing SMART collection
}
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(os.TempDir())
if err != nil || !info.IsDir() {
	return fmt.Errorf("temp dir %s unusable: %w", os.TempDir(), err)
}
probe := filepath.Join(os.TempDir(), "beszel", "smartmontools")
if err := os.MkdirAll(probe, 0o755); err != nil {
	return fmt.Errorf("cannot create smartctl dir: %w", err)
}

Type guard

func hasWritableTempDir() bool {
	f, err := os.CreateTemp("", "beszel-probe-*")
	if err != nil {
		return false
	}
	f.Close()
	os.Remove(f.Name())
	return true
}

Try / catch

path, err := ensureEmbeddedSmartctl()
if err != nil {
	var perr *fs.PathError
	if errors.As(err, &perr) {
		log.Printf("smartctl unavailable (%s: %v); SMART monitoring disabled", perr.Op, perr.Err)
	}
	return nil // degrade gracefully
}

Prevention

When it happens

Trigger: Calling agent SMART collection on Windows; smartctlOnce runs once and os.MkdirAll fails because the temp directory cannot be created or accessed.

Common situations: TMP/TEMP env vars point to a non-writable or read-only location; antivirus or endpoint protection blocks creating executables under the user temp dir; disk full; running as a service account whose temp dir is restricted.

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/89d057a24a966f0d. Report an issue: GitHub.