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
- Encrypt the secret into a secretbox/KMS envelope, base64-encode it, and set the ciphertextEnv variable instead
- Remove the plaintext variable from the production environment entirely to prevent accidental use
- Update deployment manifests/templates so they only ever populate the ciphertext form
- 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.