BookStackApp/BookStack · error · LdapException

Could not start TLS connection

Error message

Could not start TLS connection

What it means

startTls() returned false without throwing, meaning the LDAP layer reported the TLS negotiation failed but did not raise an exception. LdapService treats a falsy startTls() result as fatal and throws this message. Like error 10, the underlying LDAP diagnostic was not attached to this exception.

Source

Thrown at app/Access/LdapService.php:265

        }

        // Set any required options
        if ($this->config['version']) {
            $this->ldap->setVersion($ldapConnection, $this->config['version']);
        }

        // Start and verify TLS if it's enabled
        if ($this->config['start_tls']) {
            try {
                $started = $this->ldap->startTls($ldapConnection);
            } catch (\Exception $exception) {
                $error = $exception->getMessage() . ' :: ' . ldap_error($ldapConnection);
                ldap_get_option($ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $detail);
                Log::info("LDAP STARTTLS failure: {$error} {$detail}");
                throw new LdapException('Could not start TLS connection. Further details in the application log.');
            }
            if (!$started) {
                throw new LdapException('Could not start TLS connection');
            }
        }

        $this->ldapConnection = $ldapConnection;

        return $this->ldapConnection;
    }

    /**
     * Configure TLS CA certs globally for ldap use.
     * This will detect if the given path is a directory or file, and set the relevant
     * LDAP TLS options appropriately otherwise throw an exception if no file/folder found.
     *
     * Note: When using a folder, certificates are expected to be correctly named by hash
     * which can be done via the c_rehash utility.
     *
     * @throws LdapException
     */

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Enable LDAP debug logging (LDAP_OPT_DEBUG_LEVEL / ldap_set_option diagnostics) and check application logs for the handshake failure cause
  2. Validate CA certificate configuration for the LDAP TLS connection
  3. Test STARTTLS directly against the server with ldapsearch -ZZ to reproduce outside PHP
  4. Switch to ldaps:// (port 636) if STARTTLS is unreliable in your environment
  5. Confirm the target host/port actually speaks LDAP and supports the StartTLS extended operation

Example fix

// before
LDAP_HOST=ldap://ldap.example.com:389 with START_TLS=true
// after (if STARTTLS keeps failing)
LDAP_HOST=ldaps://ldap.example.com:636 with START_TLS=false
Defensive patterns

Strategy: try-catch

Validate before calling

// probe the server first
exec("echo | openssl s_client -connect {$host}:389 -starttls ldap 2>/dev/null | grep 'Verify return code'", $out, $code);
if ($code !== 0) { /* STARTTLS handshake will fail */ }

Try / catch

try {
    $conn = $ldapService->validateUserCredentials($username, $password);
} catch (LdapException $e) {
    if ($e->getMessage() === 'Could not start TLS connection') {
        Log::error('STARTTLS returned false; check CA config and server TLS support');
    }
}

Prevention

When it happens

Trigger: getConnection() on the ldap:// path where $this->ldap->startTls($ldapConnection) returns false — typically the same failure modes as the thrown-exception variant (cert verification failure, server rejecting the extended operation) but surfaced as a false return instead of an exception depending on the LDAP wrapper implementation.

Common situations: Using the LdapConnection wrapper where startTls returns bool; servers that close the connection during handshake; misconfigured CA certs so verification fails silently; connecting to a plain TCP port that is not LDAP at all.

Understand the failure class

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/f4b1b11a7cdb2622. Report an issue: GitHub.