Billionmail/BillionMail · error

certificate not found in database: %v

Error message

certificate not found in database: %v

What it means

getSSLInfoFromDatabase queries the certificate table for a non-expired certificate (endtime > now, newest first). Any DB error (connection failure, missing table, query error) is wrapped as 'certificate not found in database'. Note this conflates real DB errors with the no-row case, since Scan returns nil error when zero rows match.

Source

Thrown at core/internal/service/mail_service/certificate.go:509

		Endtime     int    `json:"endtime"`
		Status      int    `json:"status"`
		Subject     string `json:"subject"`
		Issuer      string `json:"issuer"`
		NotAfter    string `json:"not_after"`
		NotBefore   string `json:"not_before"`
		Dns         string `json:"dns"`
	}

	err = g.DB().Model("letsencrypts").
		Where("dns::jsonb ? $1", public.FormatMX(domain)).
		Where("status = 1").
		Where("endtime > ?", time.Now().Unix()).
		Order("endtime desc").
		Limit(1).
		Scan(&cert)

	if err != nil {
		return certInfo, fmt.Errorf("certificate not found in database: %v", err)
	}

	if cert.Certificate == "" {
		return certInfo, fmt.Errorf("certificate content is empty in database")
	}

	// Parse certificate information
	err = gconv.Struct(acme.GetCertInfo(cert.Certificate), &certInfo)
	if err != nil {
		return certInfo, fmt.Errorf("failed to parse certificate info: %v", err)
	}

	// Set certificate content
	certInfo.CertPem = cert.Certificate
	certInfo.KeyPem = cert.PrivateKey

	return certInfo, nil
}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check DB connectivity and that the certificate table exists (run migrations).
  2. Issue/obtain a certificate first (via the SSL issuance flow) so a row with future endtime exists.
  3. Read the wrapped %v error to distinguish a DB failure from genuinely no certificate.
  4. Verify the query's Where('endtime > ?') isn't filtering out an expired cert that should be renewed.

Example fix

// before
info, err := GetSSLInfo(ctx)
if err != nil { return err }
// after
info, err := GetSSLInfo(ctx)
if err != nil {
    if strings.Contains(err.Error(), "certificate not found in database") {
        return issueNewCertificate(ctx) // bootstrap: no cert yet
    }
    return err
}
Defensive patterns

Strategy: fallback

Validate before calling

var count int
err := g.DB().Model("certificates").Where("endtime > ?", time.Now().Unix()).Count(&count)
if err != nil || count == 0 {
    // no valid certificate yet — trigger issuance instead of reading
}

Try / catch

info, err := GetSSLInfo(ctx)
if err != nil {
    if strings.Contains(err.Error(), "certificate not found in database") {
        return issueNewCertificate(ctx)
    }
    return err // real DB problem
}

Prevention

When it happens

Trigger: Calling GetSSLInfo when the certificate table is missing/unreachable, or the query itself errors. Called during SSL status checks and before sending mail that needs the current certificate.

Common situations: Fresh install where no certificate has ever been issued; database down or credentials wrong; schema migration not applied so the cert table doesn't exist.

Understand the failure class

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/6e6a74c9c432d096. Report an issue: GitHub.