knadh/listmonk · error

no captcha provider enabled

Error message

no captcha provider enabled

What it means

GenerateChallenge returns this when the Captcha instance's provider field is neither ProviderAltcha nor ProviderHCaptcha — i.e. no CAPTCHA provider was configured/enabled. The switch has no matching case, so the default branch errors out. Any caller requesting a challenge from an unconfigured Captcha gets this immediately.

Source

Thrown at internal/captcha/captcha.go:142

			SaltLength: 12,
			HMACKey:    c.altcha.HMACKey,
			Expires:    &exp,
		})
		if err != nil {
			return "", fmt.Errorf("failed to create Altcha challenge: %w", err)
		}

		challengeJSON, err := json.Marshal(challenge)
		if err != nil {
			return "", fmt.Errorf("failed to marshal Altcha challenge: %w", err)
		}

		return string(challengeJSON), nil
	case ProviderHCaptcha:
		// hCaptcha generates challenges client-side.
		return "", nil
	default:
		return "", fmt.Errorf("no captcha provider enabled")
	}
}

// Verify verifies a CAPTCHA response.
func (c *Captcha) Verify(token string) (error, bool) {
	switch c.provider {
	case ProviderAltcha:
		return c.verifyAltcha(token)
	case ProviderHCaptcha:
		return c.verifyHCaptcha(token)
	default:
		return fmt.Errorf("no captcha provider enabled"), false
	}
}

// verifyHCaptcha verifies an hCaptcha response.
func (c *Captcha) verifyHCaptcha(token string) (error, bool) {
	resp, err := c.client.PostForm(hCaptchaURL, url.Values{

View on GitHub (pinned to 670c01717d)

Solutions

  1. Set a valid provider in config (altcha or hcaptcha) and confirm the value matches the expected constant casing.
  2. Guard call sites: only call GenerateChallenge when captcha is enabled in the current environment.
  3. Add a startup validation that errors if forms require captcha but no provider is configured.
  4. Normalize the provider string in the constructor (strings.ToLower) before storing it.

Example fix

// before: config
[captcha]
# provider not set
// after
[captcha]
provider = "altcha"
complexity = 50000
hmac_key = "secret-hmac-key"
Defensive patterns

Strategy: validation

Validate before calling

switch cfg.Captcha.Provider {
case "altcha", "hcaptcha":
  // ok
default:
  return fmt.Errorf("config: captcha.provider must be 'altcha' or 'hcaptcha', got %q", cfg.Captcha.Provider)
}

Type guard

func captchaProviderEnabled(provider string) bool {
  p := strings.ToLower(provider)
  return p == "altcha" || p == "hcaptcha"
}

Try / catch

challenge, err := captcha.GenerateChallenge(ctx)
if err != nil {
  if err.Error() == "no captcha provider enabled" {
    // config bug: fail loudly, do not silently render forms without captcha
    log.Error("captcha not configured but form requires it")
    http.Error(w, "captcha misconfigured", http.StatusInternalServerError)
    return
  }
}

Prevention

When it happens

Trigger: Calling GenerateChallenge on a Captcha built with an empty/unknown provider string, or a New constructor that skipped provider configuration because the captcha config section was absent or misspelled (e.g. provider="Altcha" vs "altcha" casing mismatch).

Common situations: Config file lacks the [captcha] section or provider key; provider value typo or wrong casing; captcha intentionally disabled in some environments but code paths still call GenerateChallenge unconditionally.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/26441a716607b8e4. Report an issue: GitHub.