crowdsecurity/crowdsec · critical

failed to marshal challenge cookie proto: %w

Error message

failed to marshal challenge cookie proto: %w

What it means

sealCookieV0 serializes the pb.ChallengeCookie envelope with proto.Marshal before encrypting it. This error means protobuf marshaling failed — for a plain generated message like ChallengeCookie, this is essentially unreachable (proto.Marshal only errors on required fields unset or internal wire issues), so it is a defensive internal check.

Source

Thrown at pkg/appsec/challenge/crypto.go:140

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("failed to create cipher: %w", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create GCM: %w", err)
	}

	// Reject an over-limit envelope before marshaling it.
	maxPlaintext := maxCookieLen/4*3 - 1 - gcm.NonceSize() - gcm.Overhead()
	if plaintextLen := cookiePlaintextFixedHeaderLen + len(reason) + proto.Size(envelope); plaintextLen > maxPlaintext {
		return "", fmt.Errorf("%w: plaintext=%d > %d", ErrCookieTooLarge, plaintextLen, maxPlaintext)
	}

	envelopeBytes, err := proto.Marshal(envelope)
	if err != nil {
		return "", fmt.Errorf("failed to marshal challenge cookie proto: %w", err)
	}

	nonce := make([]byte, gcm.NonceSize())
	if _, err := rand.Read(nonce); err != nil {
		return "", fmt.Errorf("failed to generate nonce: %w", err)
	}

	// Build the plaintext: not_after_be8 || flags || reason_len_be || reason || envelope
	plaintext := make([]byte, 0, cookiePlaintextFixedHeaderLen+len(reason)+len(envelopeBytes))

	var notAfterBytes [8]byte
	binary.BigEndian.PutUint64(notAfterBytes[:], uint64(notAfter))
	plaintext = append(plaintext, notAfterBytes[:]...)

	plaintext = append(plaintext, flags)

	var reasonLenBytes [2]byte
	binary.BigEndian.PutUint16(reasonLenBytes[:], uint16(len(reason)))

View on GitHub (pinned to 909b515798)

Solutions

  1. Inspect the wrapped proto error message for the failing field.
  2. Verify the pb.ChallengeCookie passed to sealCookieV0 is a valid, non-nil generated message.
  3. Regenerate the pb package if the generated code was hand-edited or is from a mismatched .proto version.
Defensive patterns

Strategy: validation

Validate before calling

if envelope == nil {
    return errors.New("nil challenge cookie envelope")
}

Type guard

func validEnvelope(e *pb.ChallengeCookie) bool { return e != nil && proto.Size(e) >= 0 }

Try / catch

if err != nil {
    return fmt.Errorf("proto marshal: %w", err)
}

Prevention

When it happens

Trigger: Calling sealCookieV0 with an envelope that proto.Marshal cannot serialize — practically only with a nil or corrupt generated message, or a message with unpopulated required fields in proto2. All in-repo callers pass a valid &pb.ChallengeCookie{}.

Common situations: Effectively never in supported flows; would require a malformed protobuf type or corrupted reflection state.

Related errors


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