juanfont/headscale · error

creating logs directory: %w

Error message

creating logs directory: %w

What it means

Returned by runTestContainer when os.MkdirAll(absLogsDir, 0o755) fails while creating control_logs/<runID>. Filesystem causes: a parent directory owned by another user (permission denied), a non-directory file occupying part of the path, or read-only filesystem. Distinct from error 298: here the absolute path resolved fine but creation failed.

Source

Thrown at cmd/hi/docker.go:62

	runID := dockertestutil.GenerateRunID()
	containerName := "headscale-test-suite-" + runID
	logsDir := filepath.Join(config.LogsDir, runID)

	if config.Verbose {
		log.Printf("Run ID: %s", runID)
		log.Printf("Container name: %s", containerName)
		log.Printf("Logs directory: %s", logsDir)
	}

	absLogsDir, err := filepath.Abs(logsDir)
	if err != nil {
		return fmt.Errorf("getting absolute path for logs directory: %w", err)
	}

	const dirPerm = 0o755
	if err := os.MkdirAll(absLogsDir, dirPerm); err != nil { //nolint:noinlineerr
		return fmt.Errorf("creating logs directory: %w", err)
	}

	if config.CleanBefore {
		if config.Verbose {
			log.Printf("Running pre-test cleanup...")
		}

		err := cleanupBeforeTest(ctx)
		if err != nil && config.Verbose {
			log.Printf("Warning: pre-test cleanup failed: %v", err)
		}
	}

	goTestCmd := buildGoTestCommand(config)
	if config.Verbose {
		log.Printf("Command: %s", strings.Join(goTestCmd, " "))
	}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Inspect the path: ls -ld control_logs — confirm it is a directory you can write
  2. Fix ownership: sudo chown -R $USER control_logs (common after root runs)
  3. Remove any file occupying the directory name: rm control_logs if it is a regular file
  4. Check disk space and mount options (read-only workspace) in CI

Example fix

# before: root-owned logs dir from an earlier sudo run
ls -ld control_logs   # drwxr-xr-x root root
go run ./cmd/hi run TestACL   # creating logs directory: permission denied

# after
sudo chown -R "$USER" control_logs
go run ./cmd/hi run TestACL
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the logs root is a writable directory before test runs.
const perm = 0o755
if err := os.MkdirAll("control_logs", perm); err != nil {
	log.Fatalf("cannot create logs dir: %v", err)
}
if fi, err := os.Stat("control_logs"); err != nil || !fi.IsDir() {
	log.Fatal("control_logs exists but is not a directory")
}

Type guard

func isWritableDir(path string) bool {
	fi, err := os.Stat(path)
	return err == nil && fi.IsDir() && fi.Mode().Perm()&0o200 != 0
}

Try / catch

Inspect the wrapped error: os.IsPermission → fix ownership/permissions; os.IsExist with a non-directory → remove the blocking file; read-only filesystem → remount or relocate LogsDir.

Prevention

When it happens

Trigger: control_logs (or a path component) exists as a regular file; the repo is on a read-only mount; earlier runs done as root left root-owned directories; disk quota exhausted.

Common situations: Mixing sudo and non-sudo test runs creating root-owned control_logs; CI containers mounting the workspace read-only; a stray file named control_logs; full disks or quotas.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/c2770eb1d40c8a4f. Report an issue: GitHub.