projectdiscovery/katana · error

could not change ownership of chrome data directory

Error message

could not change ownership of chrome data directory

What it means

Thrown when os.Chown(tempDir, uid, gid) fails after the temp chrome data directory was created for ChromeUser. Katana removes the directory and aborts because Chrome would otherwise run with the wrong profile ownership. Typical causes are the process lacking CAP_CHOWN/root privileges or a nonexistent uid/gid pair.

Source

Thrown at pkg/engine/headless/browser/browser.go:401

			var err error
			tempDir, err = os.MkdirTemp(l.opts.ChromeUser.HomeDir, "chrome-data-*")
			if err != nil {
				return nil, errors.Wrap(err, "could not create temporary chrome data directory")
			}

			uid, err := strconv.Atoi(l.opts.ChromeUser.Uid)
			if err != nil {
				_ = os.RemoveAll(tempDir)
				return nil, errors.Wrap(err, "invalid user ID")
			}
			gid, err := strconv.Atoi(l.opts.ChromeUser.Gid)
			if err != nil {
				_ = os.RemoveAll(tempDir)
				return nil, errors.Wrap(err, "invalid group ID")
			}
			if err := os.Chown(tempDir, uid, gid); err != nil {
				_ = os.RemoveAll(tempDir)
				return nil, errors.Wrap(err, "could not change ownership of chrome data directory")
			}
			shouldCleanupTempDir = true
		} else {
			var err error
			tempDir, err = os.MkdirTemp("", "katana-chrome-data-*")
			if err != nil {
				return nil, errors.Wrap(err, "could not create temporary chrome data directory")
			}
			shouldCleanupTempDir = true
		}
	}

	browser, err := l.launchBrowserWithDataDir(tempDir)
	if err != nil {
		if shouldCleanupTempDir {
			_ = os.RemoveAll(tempDir)
		}
		return nil, err

View on GitHub (pinned to e3e742739c)

Solutions

  1. Run the process as root or with CAP_CHOWN when ChromeUser ownership change is required (Docker --user root or securityContext capabilities).
  2. Prefer running the process itself as the chrome user so no chown to a foreign uid is needed.
  3. Verify the uid/gid exist: getent passwd <uid>; getent group <gid>.
  4. Ensure the filesystem backing the temp dir supports chown and the parent directory is writable.
  5. If a dedicated user is unnecessary, set ChromeUser=nil to skip the chown path entirely.

Example fix

// before
# k8s pod running as 1000, ChromeUser uid 998 -> chown denied
// after
securityContext:
  runAsUser: 998
  runAsGroup: 998
Defensive patterns

Strategy: validation

Validate before calling

u := opts.ChromeUser
uid, _ := strconv.Atoi(u.Uid)
if os.Geteuid() != 0 && os.Geteuid() != uid {
    return fmt.Errorf("cannot chown to %s:%s: run as root or as uid %d", u.Uid, u.Gid, uid)
}

Type guard

func canChownTo(uid int) bool {
    return os.Geteuid() == 0 || os.Geteuid() == uid
}

Try / catch

page, err := l.createBrowserPageFunc(...)
if err != nil && strings.Contains(err.Error(), "change ownership of chrome data directory") {
    l.opts.ChromeUser = nil // retry without ownership change
    page, err = l.createBrowserPageFunc(...)
}

Prevention

When it happens

Trigger: ChromeUser set with a uid/gid the current process may not chown to: not running as root and target uid != current uid, target uid/gid does not exist, or an immutable/read-only filesystem.

Common situations: Docker/Kubernetes containers without root or CAP_CHOWN while using a dedicated chrome user; user-namespace remapping making the uid invalid; running katana as a non-root user for a different chrome user.

Related errors


AI-assisted analysis of projectdiscovery/katana@e3e742739c (2026-09-03). Data as JSON: /api/errors/d63f9f4127a5fb66. Report an issue: GitHub.