juicedata/juicefs · critical
decryt key: %s
Error message
decryt key: %s
What it means
During encrypted-object decryption, the data-key portion of the ciphertext is itself decrypted with the key encryptor (RSA); if that inner decryption fails, Decrypt wraps the underlying error as "decryt key: ...". The stored object cannot be read with the configured key.
Source
Thrown at pkg/object/encrypt.go:274
}
func (e *dataEncryptor) Decrypt(ciphertext []byte) ([]byte, error) {
if len(ciphertext) < 3 {
return nil, fmt.Errorf("received encrypted text length is less than 3, the object is corrupted")
}
keyLen := int(ciphertext[0])<<8 + int(ciphertext[1])
nonceLen := int(ciphertext[2])
if 3+keyLen+nonceLen >= len(ciphertext) {
return nil, fmt.Errorf("malformed ciphertext: %d %d", keyLen, nonceLen)
}
ciphertext = ciphertext[3:]
cipherkey := ciphertext[:keyLen]
nonce := ciphertext[keyLen : keyLen+nonceLen]
ciphertext = ciphertext[keyLen+nonceLen:]
key, err := e.keyEncryptor.Decrypt(cipherkey)
if err != nil {
return nil, errors.New("decryt key: " + err.Error())
}
aead, err := e.aead(key)
if err != nil {
return nil, err
}
return aead.Open(ciphertext[:0], nonce, ciphertext, nil)
}
// MaxOverhead returns the maximum number of extra bytes that Encrypt can add.
// Layout is:
//
// 2 bytes wrapped-key length
// 1 byte nonce length
// wrapped encrypted data key
// nonce
// AEAD tag
func (e *dataEncryptor) MaxOverhead() int {
aead, err := e.aead(make([]byte, e.keyLen))View on GitHub (pinned to c9a67b23e8)
Solutions
- Supply the correct, original RSA private key (and JFS_RSA_PASSPHRASE) that corresponds to the key used at write time.
- Read the wrapped underlying error after 'decryt key:' to identify whether it is a wrong key vs. corrupt ciphertext.
- Verify object integrity in the bucket (re-upload/re-sync the affected blocks if the storage is damaged).
- If keys were rotated, keep old private keys available to read historical objects.
Example fix
// before juicefs mount sqlite3://test.db /mnt/jfs # with wrong key.pem // after: mount with the original key pair used for encryption JFS_RSA_PASSPHRASE='orig-secret' juicefs mount -e --encrypt-key original.pem sqlite3://test.db /mnt/jfs
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the key decrypts its own key-envelope before mounting production
// (round-trip a test object at setup time)
rc, err := store.Get(ctx, testKey, 0, -1)
if err != nil { return fmt.Errorf("encryption key mismatch: %w", err) } Try / catch
data, err := encStore.Read(ctx, key)
if err != nil {
if strings.Contains(err.Error(), "decryt key:") {
return fmt.Errorf("wrong or lost RSA key for this volume; restore original key/passphrase: %w", err)
}
return err
} Prevention
- Back up the RSA private key and its passphrase with the volume config — data is unrecoverable without them.
- Never rotate encryption keys in place; keep old keys to read historical objects.
- Do a small write/read round-trip after changing encryption settings.
When it happens
Trigger: Reading an encrypted object when the RSA private key does not match the public key used to encrypt the data key, the ciphertext is truncated/corrupted, or the keyEncryptor.Decrypt fails due to wrong passphrase-derived key.
Common situations: Changing or losing the RSA key pair after writing encrypted data; JFS_RSA_PASSPHRASE wrong so the private key decrypts but yields a wrong key; corrupted object blocks in object storage; mixing volumes with the same bucket but different encryption keys.
Related errors
- passphrase is required to private key
- failed to parse PEM block containing the key
- format decrypt: %s
- %w: please set the 'JFS_RSA_PASSPHRASE' environment variable
- parse private key: %s
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/5d40214532b70570.
Report an issue: GitHub.