syncthing/syncthing · error
certificate has expired
Error message
certificate has expired
What it means
Raised by the self-signed HTTPS certificate sanity check in the REST API layer. After confirming the leaf certificate is Syncthing's own newer-style self-signed certificate (zero DNS SANs, or a single SAN equal to the issuer's CommonName), the code compares leaf.NotAfter to time.Now(); if the certificate validity has passed, it returns errors.New("certificate has expired"). It signals that the GUI's TLS certificate is dead and must be regenerated before modern clients will trust the endpoint.
Source
Thrown at lib/api/api.go:1893
if leaf.Subject.String() != leaf.Issuer.String() || len(leaf.IPAddresses) != 0 {
// The certificate is not self signed, or has IP attributes we don't
// add, so we leave it alone.
return nil
}
if len(leaf.DNSNames) > 1 {
// The certificate has more DNS SANs attributes than we ever add, so
// we leave it alone.
return nil
}
if len(leaf.DNSNames) == 1 && leaf.DNSNames[0] != leaf.Issuer.CommonName {
// The one SAN is different from the issuer, so it's not one of our
// newer self signed certificates.
return nil
}
if leaf.NotAfter.Before(time.Now()) {
return errors.New("certificate has expired")
}
if leaf.NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) {
return errors.New("certificate will soon expire")
}
// On macOS, check for certificates issued on or after July 1st, 2019,
// with a longer validity time than 825 days.
cutoff := time.Date(2019, 7, 1, 0, 0, 0, 0, time.UTC)
if build.IsDarwin &&
leaf.NotBefore.After(cutoff) &&
leaf.NotAfter.Sub(leaf.NotBefore) > 825*24*time.Hour {
return errors.New("certificate incompatible with macOS 10.15 (Catalina)")
}
return nil
}
func errorStringMap(errs map[string]error) map[string]*string {View on GitHub (pinned to 058bcd7334)
Solutions
- Stop Syncthing, delete the expired certificate and key in the config directory (https-cert.pem/https-key.pem or certificate.pem/key.pem), and restart to generate a fresh self-signed certificate.
- Check the system clock (date, NTP status); if the clock is wrong, fix it before regenerating certificates.
- Alternatively replace the self-signed certificate with a proper one via the GUI HTTPS settings if clients must not re-accept a new cert.
- Re-accept the new certificate in the browser/API client afterwards, since the device identity may change if the API cert is also used for the device ID.
Defensive patterns
Strategy: validation
Validate before calling
// Check expiry before starting the GUI / making requests:
leaf, err := getLeafCert(certPath)
if err == nil && leaf.NotAfter.Before(time.Now()) {
// regenerate certificate before use
} Try / catch
if err := checkCertificate(cert); err != nil {
if strings.Contains(err.Error(), "certificate has expired") {
// regenerate cert (delete cert+key, restart) instead of failing
}
} Prevention
- Monitor certificate NotAfter dates like any other TLS cert.
- Keep host clocks NTP-synced so validity windows are evaluated correctly.
- Regenerate certs during scheduled maintenance before they lapse.
When it happens
Trigger: The certificate-check function runs against the GUI's certificate.pem/https-cert.pem leaf: the cert is recognized as Syncthing-issued (SAN rules match) and leaf.NotAfter is before time.Now(). Typically after a long downtime or a system clock moved past the validity period.
Common situations: Devices that were offline or retired for longer than the certificate lifetime (e.g. a year+); VMs restored from an old snapshot with an expired cert; RTC battery failure pushing the clock forward.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- certificate will soon expire
- certificate incompatible with macOS 10.15 (Catalina)
- create certificate: %w
- modify config: %w
- failed to set GUI authentication password: %w
AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15).
Data as JSON: /api/errors/fd0c861703097eed.
Report an issue: GitHub.