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

  1. Supply the correct, original RSA private key (and JFS_RSA_PASSPHRASE) that corresponds to the key used at write time.
  2. Read the wrapped underlying error after 'decryt key:' to identify whether it is a wrong key vs. corrupt ciphertext.
  3. Verify object integrity in the bucket (re-upload/re-sync the affected blocks if the storage is damaged).
  4. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/5d40214532b70570. Report an issue: GitHub.