kgretzky/evilginx2 · error

login: `domain` field cannot be empty

Error message

login: `domain` field cannot be empty

What it means

After reading `login.domain`, phishlet loading verifies it equals one of the hostnames built from the `proxy_hosts` entries (`orig_subdomain` + "." + `domain`, case-insensitive). An empty `login.domain` value fails this cross-check and aborts loading.

Source

Thrown at core/phishlet.go:643

	if p.username.tp == "" {
		p.username.tp = "post"
	}
	p.password.tp = fp.Credentials.Password.Type
	if p.password.tp == "" {
		p.password.tp = "post"
	}
	p.username.key_s = p.paramVal(*fp.Credentials.Username.Key)
	p.password.key_s = p.paramVal(*fp.Credentials.Password.Key)

	if fp.LoginItem.Domain == nil {
		return fmt.Errorf("login: missing `domain` field")
	}
	if fp.LoginItem.Path == nil {
		return fmt.Errorf("login: missing `path` field")
	}
	p.login.domain = p.paramVal(*fp.LoginItem.Domain)
	if p.login.domain == "" {
		return fmt.Errorf("login: `domain` field cannot be empty")
	}
	login_domain_ok := false
	for _, h := range p.proxyHosts {
		var check_host string
		if h.orig_subdomain != "" {
			check_host = h.orig_subdomain + "."
		}
		check_host += h.domain
		if strings.ToLower(check_host) == strings.ToLower(p.login.domain) {
			login_domain_ok = true
			break
		}
	}
	if !login_domain_ok {
		return fmt.Errorf("login: `domain` must contain a value of one of the hostnames (`orig_subdomain` + `domain`) defined in `proxy_hosts` section")
	}

	p.login.path = p.paramVal(*fp.LoginItem.Path)

View on GitHub (pinned to 4c0988a1d9)

Solutions

  1. Set `login.domain` to the full original hostname, e.g. `accounts.example.com`, matching a proxy_hosts entry.
  2. Confirm the phishlet's parameters (if `domain` uses `{param}` substitution) are supplied and non-empty.
  3. Ensure the value matches exactly one of `orig_subdomain.domain` combinations spelled in proxy_hosts.
  4. Reload the phishlet to continue validation.

Example fix

// before
proxy_hosts:
  - phish_sub: acct
    orig_sub: accounts
    domain: example.com
login:
  domain: ''
// after
login:
  domain: accounts.example.com
Defensive patterns

Strategy: validation

Validate before calling

d := strings.ToLower(pl.Login.Domain)
ok := false
for _, h := range pl.ProxyHosts {
    if strings.ToLower(h.OrigSubdomain+"."+h.Domain) == d { ok = true; break }
}
if pl.Login.Domain == "" || !ok {
    return errors.New("login.domain empty or not among proxy_hosts hostnames")
}

Type guard

func loginDomainMatches(l *LoginItem, hosts []ProxyHost) bool {
    if l == nil || l.Domain == nil || *l.Domain == "" { return false }
    for _, h := range hosts {
        if strings.EqualFold(h.OrigSubdomain+"."+h.Domain, *l.Domain) { return true }
    }
    return false
}

Try / catch

if err := pl.Load(cfg); err != nil {
    if strings.Contains(err.Error(), "cannot be empty") {
        log.Printf("set login.domain for %s to orig_sub.domain of a proxy_host", pl.Name)
    }
}

Prevention

When it happens

Trigger: Phishlet YAML with `login:` `domain: ""` (or `domain:` with no value) — the key exists but paramVal resolves it to an empty string.

Common situations: Author leaving a placeholder blank intending to fill it later; environment variable/param substitution (`{...}` placeholders) resolving to empty because the phishlet parameter was not provided.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05). Data as JSON: /api/errors/a5b0d3d9162e4cff. Report an issue: GitHub.