cloudflare/cloudflared · error

error opening file %s:%w

Error message

error opening file %s:%w

What it means

CopyFilesFromDirectory in the diagnostic package tries to open the tunnel's default log file (cloudflared.log) inside the given directory to copy it into a diagnostic bundle. When os.Open fails, it wraps the underlying OS error (ENOENT, EACCES, etc.) with this message and returns an empty path, aborting log collection. It reflects that the log file could not be read from disk.

Source

Thrown at diagnostic/log_collector_utils.go:103

	defer func() { _ = outputHandle.Close() }()

	for _, file := range files {
		// nolint: gosec
		logHandle, err := os.Open(filepath.Join(path, file.Name()))
		if err != nil {
			return "", fmt.Errorf("error opening file %s: %w", file.Name(), err)
		}
		_, err = io.Copy(outputHandle, logHandle)
		_ = logHandle.Close()
		if err != nil {
			return "", fmt.Errorf("error copying file %s: %w", file.Name(), err)
		}
	}

	// nolint: gosec
	logHandle, err := os.Open(filepath.Join(path, defaultLogFilename))
	if err != nil {
		return "", fmt.Errorf("error opening file %s:%w", defaultLogFilename, err)
	}
	defer func() { _ = logHandle.Close() }()

	_, err = io.Copy(outputHandle, logHandle)
	if err != nil {
		return "", fmt.Errorf("error copying file %s:%w", logHandle.Name(), err)
	}

	return outputHandle.Name(), nil
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Verify the log file exists at the expected path before collecting: os.Stat(filepath.Join(path, defaultLogFilename)).
  2. Check file permissions and ownership; run collection as a user that can read the log directory (or adjust ACLs/SELinux context).
  3. Pass the correct directory path containing the log file to CopyFilesFromDirectory.
  4. Handle the wrapped *fs.PathError in the caller to distinguish not-found from permission issues.

Example fix

// before
out, err := CopyFilesFromDirectory(ctx, logDir)
if err != nil {
	return err
}
// after
logPath := filepath.Join(logDir, diagnostic.DefaultLogFilename)
if _, err := os.Stat(logPath); errors.Is(err, fs.ErrNotExist) {
	// skip log collection, no log file present
	return nil
}
out, err := CopyFilesFromDirectory(ctx, logDir)
if err != nil {
	return fmt.Errorf("collect logs: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

logPath := filepath.Join(dir, "cloudflared.log")
if _, err := os.Stat(logPath); err != nil {
	if errors.Is(err, fs.ErrNotExist) { /* skip collection */ }
	if errors.Is(err, fs.ErrPermission) { /* fix perms or run as root */ }
}

Type guard

func logFileReadable(dir, name string) bool {
	f, err := os.Open(filepath.Join(dir, name))
	if err != nil { return false }
	_ = f.Close()
	return true
}

Try / catch

out, err := CopyFilesFromDirectory(ctx, dir)
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) {
	// degrade gracefully: proceed without log file
}

Prevention

When it happens

Trigger: Calling CopyFilesFromDirectory (directly or via collectLogs) when the log file does not exist at filepath.Join(path, defaultLogFilename), the path points to a directory without the expected file, or the file lacks read permission.

Common situations: Diagnostic collection on a system where cloudflared was never run with logging enabled; log directory rotated/deleted before collection; running under a service account that cannot read the log directory (e.g. /var/log/cloudflared owned by root); SELinux/AppArmor denials.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/9d71f35dcdc16675. Report an issue: GitHub.