JuliusBrussee/caveman · error

secretbox: production refuses plaintext %s; use %s

Error message

secretbox: production refuses plaintext %s; use %s

What it means

ResolveEnvironmentSecret is running in production and found the plaintext environment variable set. Production must receive secrets as ciphertext envelopes, so the plaintext is refused outright; the message names both the offending plaintext variable and the ciphertext variable to use instead.

Source

Thrown at shared/platform/secretbox/secretbox.go:171

		return plaintext, nil
	}
	return Decrypt(envelope)
}

func useKMS() bool {
	return strings.EqualFold(strings.TrimSpace(os.Getenv("CAVE_KMS_PROVIDER")), kms.ProviderScaleway)
}

// ResolveEnvironmentSecret loads a boot-time secret. In production plaintext
// environment variables are rejected: operators must provide a base64-encoded
// secretbox/KMS envelope in ciphertextEnv. Local development may continue using
// plaintextEnv. An entirely absent optional secret returns an empty string.
func ResolveEnvironmentSecret(plaintextEnv, ciphertextEnv string) (string, error) {
	plain := strings.TrimSpace(os.Getenv(plaintextEnv))
	encoded := strings.TrimSpace(os.Getenv(ciphertextEnv))
	production := runtimeenv.IsProduction()
	if production && plain != "" {
		return "", fmt.Errorf("secretbox: production refuses plaintext %s; use %s", plaintextEnv, ciphertextEnv)
	}
	if encoded == "" {
		return plain, nil
	}
	wrapped, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return "", fmt.Errorf("secretbox: %s is not valid base64", ciphertextEnv)
	}
	decrypted, err := Decrypt(wrapped)
	if err != nil {
		return "", fmt.Errorf("secretbox: decrypt %s: %w", ciphertextEnv, err)
	}
	if len(decrypted) == 0 {
		return "", fmt.Errorf("secretbox: %s decrypted to an empty secret", ciphertextEnv)
	}
	return string(decrypted), nil
}

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Encrypt the secret into a secretbox/KMS envelope, base64-encode it, and set the ciphertextEnv variable instead
  2. Remove the plaintext variable from the production environment entirely to prevent accidental use
  3. Update deployment manifests/templates so they only ever populate the ciphertext form
  4. For local development nothing changes — plaintextEnv remains accepted outside production
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at shared/platform/secretbox/secretbox.go:171 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/0362c793d57a68f7. Report an issue: GitHub.