projectdiscovery/katana · error

invalid group ID

Error message

invalid group ID

What it means

Thrown when strconv.Atoi fails to parse ChromeUser.Gid. Same as the UID case but for the group ID: katana cannot chown the temp chrome data directory, so it deletes the directory and aborts page creation.

Source

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

			// Use user-provided data directory (preserve sessions/cookies)
			tempDir = l.opts.UserDataDir
			shouldCleanupTempDir = false
		} else if l.opts.ChromeUser != nil {
			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 {

View on GitHub (pinned to e3e742739c)

Solutions

  1. Use user.Lookup("chrome") / user.LookupGroup to obtain properly populated Uid and Gid strings.
  2. Validate both fields up front with strconv.Atoi and fall back to ChromeUser=nil when invalid.
  3. Correct the account's primary group in /etc/passwd (or the NSS/LDAP source) if Gid is missing.
  4. If ownership change is not required, leave ChromeUser nil to use the default temp directory.

Example fix

// before
opts.ChromeUser = &user.User{Username: "chrome", Uid: "1000"} // Gid missing
// after
u, err := user.Lookup("chrome")
if err != nil {
    return err
}
if _, err := strconv.Atoi(u.Gid); err != nil {
    return fmt.Errorf("gid for user chrome is not numeric: %q", u.Gid)
}
opts.ChromeUser = u
Defensive patterns

Strategy: validation

Validate before calling

if opts.ChromeUser != nil {
    if _, err := strconv.Atoi(opts.ChromeUser.Gid); err != nil {
        return fmt.Errorf("ChromeUser.Gid %q is not numeric", opts.ChromeUser.Gid)
    }
}

Type guard

func hasNumericGid(u *user.User) bool {
    _, err := strconv.Atoi(u.Gid)
    return err == nil
}

Try / catch

page, err := l.createBrowserPageFunc(...)
if err != nil && strings.Contains(err.Error(), "invalid group ID") {
    l.opts.ChromeUser = nil // retry without dedicated chrome user
    page, err = l.createBrowserPageFunc(...)
}

Prevention

When it happens

Trigger: ChromeUser.Gid is not a valid decimal integer string — empty or non-numeric — when the user.User struct was constructed manually or came from a directory service that did not populate Gid.

Common situations: Hand-built user.User missing Gid; NSS/LDAP lookups returning incomplete entries; pasting a uid into the Gid field with a typo; Windows hosts where user.Lookup semantics differ.

Related errors


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