Billionmail/BillionMail · error

Failed to create ACME client: {}

Error message

Failed to create ACME client: {}

What it means

lego.NewClient(config) failed while constructing the ACME client from the previously obtained *lego.Config. lego errors here when the config's private key is invalid or the user registration data is unusable — it cannot assemble a client without a workable key. The code wraps the library error with a localized message.

Source

Thrown at core/internal/service/acme/acme.go:392

	logFile := GetLogFile(ctx)
	SetLog(logFile)
	defer CloseLog(logFile)

	// Get user information
	myUser, err := GetMyUser(ctx, email)
	if err != nil {
		return "", "", err
	}

	// Get configuration
	config := GetConfig(myUser)
	if config == nil {
		return "", "", errors.New(public.LangCtx(ctx, "Failed to get configuration"))
	}

	client, err := lego.NewClient(config)
	if err != nil {
		return "", "", errors.New(public.LangCtx(ctx, "Failed to create ACME client: {}", err.Error()))
	}

	// Set verification method
	if vtype == "http" {
		// Assume the HTTP server is already running and properly configured
		// to handle the challenge requests
		err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("127.0.0.1", "60880"))
		if err != nil {
			return "", "", errors.New(public.LangCtx(ctx, "Failed to set HTTP verification: {}", err.Error()))
		}
	} else if vtype == "dns" && dnsProvider != "" {
		// Set DNS verification - same as in the standard ApplySSL function
		switch dnsProvider {
		case "tencentcloud":
			err = SetDnsTencentcloud(ctx, client, dnsProviderToken)
			if err != nil {
				return "", "", errors.New(public.LangCtx(ctx, "Failed to set Tencent Cloud DNS verification: {}", err.Error()))
			}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the wrapped err.Error() — lego states whether the key is nil, malformed, or unsupported.
  2. Regenerate the ACME account key (ECDSA P-256 or RSA 2048+) and re-register the account.
  3. Verify the stored key file/record loads and parses (pem.Decode / x509.ParseECPrivateKey) before calling ApplySSLWithExistingServer.
  4. Restore the account key from backup if it was corrupted in storage.

Example fix

// before
myUser.key = loadRawKeyBytes() // raw bytes, not parsed
client, err := lego.NewClient(config)
// after
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
    return "", "", err
}
myUser.key = key
client, err := lego.NewClient(config)
Defensive patterns

Strategy: try-catch

Validate before calling

func accountKeyValid(key crypto.PrivateKey) error {
    switch k := key.(type) {
    case *ecdsa.PrivateKey:
        return k.Validate()
    case *rsa.PrivateKey:
        return k.Validate()
    default:
        return fmt.Errorf("unsupported account key type %T", key)
    }
}

Type guard

func usableAccountKey(u *MyUser) bool {
    switch u.GetPrivateKey().(type) {
    case *ecdsa.PrivateKey, *rsa.PrivateKey:
        return true
    default:
        return false
    }
}

Try / catch

client, err := lego.NewClient(config)
if err != nil {
    log.Printf("lego.NewClient failed: %v — regenerating account key", err)
    if regErr := reRegisterAcmeAccount(ctx, email); regErr != nil {
        return "", "", regErr
    }
    return "", "", errors.New("ACME client rebuilt with new account key; retry")
}

Prevention

When it happens

Trigger: Any ApplySSLWithExistingServer caller where the MyUser's private key is nil, corrupt, or of an unsupported type, so lego.NewClient rejects it during client construction.

Common situations: A persisted ACME account key file that was truncated/corrupted by disk issues; keys generated with unsupported algorithms or wrong PEM encoding; a user record deserialized from storage missing the key field; refactors that changed how the key is stored/loaded.

Related errors


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