juanfont/headscale · error

reading logs directory: %w

Error message

reading logs directory: %w

What it means

Returned by cleanupSuccessfulTestArtifacts when os.ReadDir on the control_logs directory fails. Filesystem-level causes: the directory does not exist (no runs recorded yet), permission denied, or the path is not a directory. It propagates from `hi cleanup` when pruning artifacts of successful runs.

Source

Thrown at cmd/hi/cleanup.go:342

		fmt.Printf("Removed Go module cache volume: %s\n", volumeName)
	}

	return nil
}

// cleanupSuccessfulTestArtifacts removes artifacts from successful test runs to save disk space.
// This function removes large artifacts that are mainly useful for debugging failures:
// - Database dumps (.db files)
// - Profile data (pprof directories)
// - MapResponse data (mapresponses directories)
// - Prometheus metrics files
//
// It preserves:
// - Log files (.log) which are small and useful for verification.
func cleanupSuccessfulTestArtifacts(logsDir string, verbose bool) error {
	entries, err := os.ReadDir(logsDir)
	if err != nil {
		return fmt.Errorf("reading logs directory: %w", err)
	}

	var (
		removedFiles, removedDirs int
		totalSize                 int64
	)

	for _, entry := range entries {
		name := entry.Name()
		fullPath := filepath.Join(logsDir, name)

		if entry.IsDir() {
			// Remove pprof and mapresponses directories (typically large)
			// These directories contain artifacts from all containers in the test run
			if name == "pprof" || name == "mapresponses" {
				size, sizeErr := getDirSize(fullPath)
				if sizeErr == nil {
					totalSize += size

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check that the logs directory exists and is readable: ls control_logs/
  2. Create it or run one integration test first so it gets populated
  3. Fix ownership/permissions (chown/chmod) if tests ran as another user
  4. Remove blocking non-directory entries at the expected path

Example fix

# before: no runs yet, directory absent
go run ./cmd/hi cleanup   # reading logs directory: ...

# after
mkdir -p control_logs
go run ./cmd/hi cleanup
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the logs dir exists and is a directory before cleanup.
if fi, err := os.Stat(logsDir); err != nil {
	fmt.Fprintln(os.Stderr, "no logs directory yet — nothing to clean")
	return nil
} else if !fi.IsDir() {
	return fmt.Errorf("%s is not a directory", logsDir)
}

Type guard

func isReadableDir(path string) bool {
	fi, err := os.Stat(path)
	return err == nil && fi.IsDir()
}

Try / catch

Treat os.IsNotExist(err) from ReadDir as a no-op (nothing to clean); other errors (permission) should be reported with the path.

Prevention

When it happens

Trigger: Running artifact cleanup when the logs directory was deleted or never created (no prior test runs); insufficient permissions on control_logs; a file existing where the directory is expected.

Common situations: Fresh checkouts before any integration run; CI workspaces wiped between jobs; directories removed by disk-cleanup scripts; ownership mismatches after running tests as root and cleaning as user.

Related errors


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