risingwavelabs/risingwave · error · PsqlError

LDAP bind failed

Error message

LDAP bind failed

What it means

Once the user's DN is found, search_and_bind binds as that DN with the end-user's password to authenticate them. simple_bind errors are mapped to this StartupError before the connection is unbound; a non-success result code from bind_result.success() is then matched separately. This is the final user-credential check — a failure means the username/password supplied by the psql client are wrong for the directory, or the DN found cannot bind.

Solutions

  1. Have the user retry with the correct directory password (this is usually simple credential failure)
  2. Verify the matched DN can bind: ldapwhoami -D 'user_dn' -w 'password'
  3. Use ldaps:// or StartTLS if the directory rejects simple binds over insecure connections
  4. Tighten search_filter so only bindable user objects match (e.g. add (objectClass=user))

Example fix

// before: filter matches groups too
search_filter = '(cn={username})'
// after: restrict to user objects
search_filter = '(&(objectClass=user)(sAMAccountName={username}))'
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm a test user DN can bind before pointing RisingWave at the directory
# ldapwhoami -H ldaps://ldap.corp:636 -D 'cn=testuser,ou=users,dc=corp,dc=com' -w 'password'

Try / catch

catch PsqlError::StartupError 'LDAP bind failed'; on invalidCredentials (49) return a clean auth-failure to the psql client rather than a startup error; log only the DN, never the password

Prevention

When it happens

Trigger: ldap.simple_bind(user_dn, password) returns Err (mapped here), or later bind_result.success() yields an error result code like invalidCredentials

Common situations: End user typed the wrong password in psql; password expired or account locked; directory disallows simple binds for user entries (e.g. AD default forbids simple bind over plaintext); the matched entry is not actually a bindable user (found a group or contact object).

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/883be8ba619df5bb. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/pgwire/src/ldap_auth.rs:532

            .await
            .map_err(|e| {
                PsqlError::StartupError(anyhow!(e).context("LDAP search failed").into())
            })?;

        // If no user found, authentication fails
        let search_entries: Vec<SearchEntry> =
            rs.0.into_iter().map(SearchEntry::construct).collect();
        if search_entries.is_empty() {
            return Ok(false);
        }

        // Attempt to bind with the user's DN and password
        let user_dn = &search_entries[0].dn;

        let bind_result = ldap
            .simple_bind(user_dn, password)
            .await
            .map_err(|e| PsqlError::StartupError(anyhow!(e).context("LDAP bind failed").into()));

        // Explicitly unbind the connection
        let _ = ldap.unbind().await;

        let bind_result = bind_result?;
        match bind_result.success() {
            Ok(_) => Ok(true),
            Err(e) => {
                tracing::error!(error = %e.as_report(), "LDAP bind unsuccessful");
                Err(PsqlError::StartupError(
                    anyhow!(e).context("LDAP bind failed").into(),
                ))
            }
        }
    }

    /// Simple bind authentication
    async fn simple_bind(&self, username: &str, password: &str) -> PsqlResult<bool> {

View on GitHub (pinned to 6469eb736d)