crowdsecurity/crowdsec · error

hex master secret decodes to %d bytes; minimum is %d

Error message

hex master secret decodes to %d bytes; minimum is %d

What it means

ParseConfiguredSecret accepts a hex-encoded master secret, and rejects it when the decoded bytes are shorter than minSecretBytes. Hex strings are parsed by character count, so a long-looking hex string can still decode to too little entropy; this error surfaces that directly.

Source

Thrown at pkg/appsec/challenge/secret.go:46

		return nil, fmt.Errorf("generate random master secret: %w", err)
	}
	return buf, nil
}

// ParseConfiguredSecret accepts a configured master secret as either a hex
// string (preferred — encodes raw bytes unambiguously) or a raw passphrase
// (fallback for human-edited configs). The result is at least minSecretBytes.
func ParseConfiguredSecret(value string) ([]byte, error) {
	if value == "" {
		return nil, errors.New("empty master secret")
	}

	// Hex form: even length, hex digits only.
	if isHex(value) {
		raw, err := hex.DecodeString(value)
		if err == nil {
			if len(raw) < minSecretBytes {
				return nil, fmt.Errorf("hex master secret decodes to %d bytes; minimum is %d", len(raw), minSecretBytes)
			}
			return raw, nil
		}
		// Fall through to passphrase handling on hex parse failure — defensive.
	}

	if len(value) < minSecretBytes {
		return nil, fmt.Errorf("passphrase master secret is %d bytes; minimum is %d", len(value), minSecretBytes)
	}

	return []byte(value), nil
}

func isHex(s string) bool {
	if s == "" || len(s)%2 != 0 {
		return false
	}
	for i := range len(s) {

View on GitHub (pinned to 909b515798)

Solutions

  1. Generate a 32-byte secret: `openssl rand -hex 32` (64 hex characters) and paste it as master_secret
  2. Count the hex characters: length/2 must be >= minSecretBytes
  3. If it was intended as a passphrase, remove hex-looking characters so it falls into the passphrase path (still must meet the byte minimum)
  4. Update distributed-deployment configs so every instance gets the corrected full-length secret

Example fix

// before: 40 hex chars -> 20 bytes -> rejected
master_secret: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
// after: 64 hex chars -> 32 bytes
master_secret: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
Defensive patterns

Strategy: validation

Validate before calling

func validHexSecretLen(s string, min int) bool {
    raw, err := hex.DecodeString(s)
    return err == nil && len(raw) >= min
}
// use: validHexSecretLen(cfg.MasterSecret, 32)

Prevention

When it happens

Trigger: BuildOptions parses a configured master_secret that isHex recognizes and hex.DecodeString decodes, but the decoded byte length is below minSecretBytes — e.g. 40 hex chars = 20 bytes.

Common situations: Operator generated a 16/20/24-byte key instead of 32; used an MD5 or SHA-1 hash output as the secret; copy-pasted a partial hex string; test cases like TestParseConfiguredSecret_HexTooShort.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/a5ab879cf1a188e1. Report an issue: GitHub.