goharbor/harbor · error

empty search dn

Error message

empty search dn

What it means

Sentinel error returned by ldap.TestConfig (POST /api/v2.0/ldap/ping) when the effective ldap_search_dn is empty — the ping body supplied no search_dn and none is stored in configuration. Unlike the password, there is no fallback: an empty bind DN is rejected outright before attempting the bind.

Source

Thrown at src/pkg/ldap/ldap.go:60

var ErrInvalidCredential = errors.New("invalid credential")

// ErrLDAPServerTimeout ...
var ErrLDAPServerTimeout = errors.New("ldap server network timeout")

// ErrLDAPPingFail ...
var ErrLDAPPingFail = errors.New("fail to ping LDAP server")

// ErrDNSyntax ...
var ErrDNSyntax = errors.New("invalid DN syntax")

// ErrInvalidFilter ...
var ErrInvalidFilter = errors.New("invalid filter syntax")

// ErrEmptyBaseDN ...
var ErrEmptyBaseDN = errors.New("empty base dn")

// ErrEmptySearchDN ...
var ErrEmptySearchDN = errors.New("empty search dn")

// Session - define a LDAP session
type Session struct {
	basicCfg models.LdapConf
	groupCfg models.GroupConf
	ldapConn *goldap.Conn
}

// NewSession create session with configs
func NewSession(basicCfg models.LdapConf, groupCfg models.GroupConf) *Session {
	return &Session{
		basicCfg: basicCfg,
		groupCfg: groupCfg,
	}
}

func formatURL(ldapURL string) (string, error) {
	var protocol, hostport string

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Include search_dn in the ping request: e.g. "cn=svc-harbor,ou=svc,dc=example,dc=com".
  2. Or save ldap_search_dn permanently first via PUT /api/v2.0/configurations, then ping without it.
  3. If your directory relies on anonymous bind, configure a low-privilege read-only account DN instead.

Example fix

# before
curl -X POST https://harbor/api/v2.0/ldap/ping -d '{"url": "ldap://ad.example.com"}'
# after
curl -X POST https://harbor/api/v2.0/ldap/ping -d '{"url": "ldap://ad.example.com", "search_dn": "cn=svc-harbor,ou=svc,dc=example,dc=com", "search_password": "<secret>"}'
Defensive patterns

Strategy: validation

Validate before calling

// Before POST /api/v2.0/ldap/ping:
if strings.TrimSpace(effectiveSearchDN(cfg, req)) == "" {
    fail("supply search_dn in the request or save ldap_search_dn first")
}

Type guard

func isLdapEmptySearchDN(err error) bool { return errors.Is(err, ldap.ErrEmptySearchDN) }

Try / catch

if errors.Is(err, ldap.ErrEmptySearchDN) { populate search_dn (request body or stored config) and re-run the ping; connection-level issues are already ruled out at this point }

Prevention

When it happens

Trigger: POST /api/v2.0/ldap/ping with a request body lacking search_dn while the stored configuration's ldap_search_dn is ""; or pinging with an explicitly empty string; the check runs right after the connection opens, so network/auth errors appear before it.

Common situations: Testing connectivity before finishing the LDAP config; directories that allow anonymous search (admins leave the DN blank) — Harbor still requires a non-empty value; config migration that dropped the DN field; form validation gaps in custom admin tooling.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/781d9e23e42a1e8b. Report an issue: GitHub.