micro/go-micro · error
decryption failed (is the key set correctly?)
Error message
decryption failed (is the key set correctly?)
What it means
NaCl secretbox.Open failed to authenticate the ciphertext with the configured symmetric key. Secretbox verifies a MAC before decrypting, so this error means the data was not encrypted with the same 32-byte key that Init loaded, or the data is corrupted.
Source
Thrown at config/secrets/secretbox/secretbox.go:70
func (s *secretBox) Encrypt(in []byte, opts ...secrets.EncryptOption) ([]byte, error) {
// no opts are expected, so they are ignored
// there must be a unique nonce for each message
var nonce [24]byte
if _, err := rand.Reader.Read(nonce[:]); err != nil {
return []byte{}, errors.Wrap(err, "couldn't obtain a random nonce from crypto/rand")
}
return secretbox.Seal(nonce[:], in, &nonce, &s.secretKey), nil
}
func (s *secretBox) Decrypt(in []byte, opts ...secrets.DecryptOption) ([]byte, error) {
// no options are expected, so they are ignored
var decryptNonce [24]byte
copy(decryptNonce[:], in[:24])
decrypted, ok := secretbox.Open(nil, in[24:], &decryptNonce, &s.secretKey)
if !ok {
return []byte{}, errors.New("decryption failed (is the key set correctly?)")
}
return decrypted, nil
}
View on GitHub (pinned to 24529f1404)
Solutions
- Ensure the same 32-byte key used by Init at encryption time is configured at decryption time; compare keys byte-for-byte across environments.
- Keep the payload intact: the first 24 bytes are the nonce and must be included; decode/encode with a stable, lossless encoding (base64) end-to-end.
- If keys rotated, keep the old key available to decrypt legacy records and migrate them to the new key.
- Verify Init succeeded with no error and that the key length check (32 bytes) passed.
Example fix
// before
decrypted, err := sb.Decrypt(storedString) // may have lost bytes
// after
raw, err := base64.StdEncoding.DecodeString(storedString)
if err != nil { return err }
decrypted, err := sb.Decrypt(raw) // same key as used to encrypt Defensive patterns
Strategy: try-catch
Validate before calling
if len(ciphertext) <= 24 {
return errors.New("payload too short: missing nonce")
} Try / catch
decrypted, err := sb.Decrypt(payload)
if err != nil {
if strings.Contains(err.Error(), "is the key set correctly") {
return fmt.Errorf("secretbox key mismatch or corrupt payload: %w", err)
}
return err
} Prevention
- Keep secretbox keys synchronized across all services sharing encrypted data
- Record a key version alongside ciphertext to support rotation
- Always store ciphertext as base64/hex, never raw bytes in string columns
- Verify the same key on both encrypt and decrypt paths with a startup self-test
When it happens
Trigger: Calling secretBox.Decrypt on data encrypted with a different key; the ciphertext's first 24 bytes (nonce) were stripped or altered; the payload was truncated or double-decoded before Decrypt.
Common situations: Two environments (staging vs production) with different SECRETBOX keys but shared stored ciphertext; key rotation where old records still use the previous key; storing ciphertext through a lossy transformation (e.g. writing bytes as a string with encoding changes).
Related errors
- sender's public key bust be provided
- incoming message couldn't be verified / decrypted
- no secret key is defined
- recepient's public key must be provided
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/e2ef6761f2b20f39.
Report an issue: GitHub.