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 stringView on GitHub (pinned to 7b2fd08cc5)
Solutions
- Include search_dn in the ping request: e.g. "cn=svc-harbor,ou=svc,dc=example,dc=com".
- Or save ldap_search_dn permanently first via PUT /api/v2.0/configurations, then ping without it.
- 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
- Always ping with the full triple: url, search_dn, search_password during setup.
- Even for anonymous-search directories, register a read-only bind DN.
- Templated configs should assert search_dn is non-empty after rendering.
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
- BAD_REQUEST
- empty password
- fail to ping LDAP server
- group does not exist
- a LDAP user group with same DN already exist
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/781d9e23e42a1e8b.
Report an issue: GitHub.