juicedata/juicefs · error

decrypt format: %s

Error message

decrypt format: %s

What it means

Wraps a failure from Format.Decrypt() inside update() when reconciling a volume format with the stored one. When only the UUID differs but encryption is configured, the new format must be decrypted with the (new) key; failure means the supplied encryption key/secret cannot decrypt the format, so the update is rejected.

Source

Thrown at pkg/meta/config.go:137

		var args []interface{}
		switch {
		case f.Name != old.Name:
			args = []interface{}{"name", old.Name, f.Name}
		case f.BlockSize != old.BlockSize:
			args = []interface{}{"block size", old.BlockSize, f.BlockSize}
		case f.Compression != old.Compression:
			args = []interface{}{"compression", old.Compression, f.Compression}
		case f.Shards != old.Shards:
			args = []interface{}{"shards", old.Shards, f.Shards}
		case f.HashPrefix != old.HashPrefix:
			args = []interface{}{"hash prefix", old.HashPrefix, f.HashPrefix}
		case f.MetaVersion != old.MetaVersion:
			args = []interface{}{"meta version", old.MetaVersion, f.MetaVersion}
		}
		if args == nil {
			if f.UUID != old.UUID {
				if err := f.Decrypt(); err != nil {
					return fmt.Errorf("decrypt format: %s", err)
				}
				f.UUID = old.UUID // UUID cannot be changed alone
				if err := f.Encrypt(); err != nil {
					return fmt.Errorf("encrypt format: %s", err)
				}
			}
		} else {
			return fmt.Errorf("cannot update volume %s from %v to %v", args...)
		}
	}
	return nil
}

func (f *Format) RemoveSecret() {
	if f.SecretKey != "" {
		f.SecretKey = "removed"
	}
	if f.SessionToken != "" {

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Supply the correct --encrypt-key (the key that can decrypt the format's secrets)
  2. Inspect the wrapped error (%s) to distinguish bad key vs malformed ciphertext
  3. If migrating keys, first decrypt with the old key, update, then re-encrypt with the new key via the proper rotation procedure

Example fix

// before
juicefs format --update --encrypt-key NEW_KEY meta-url
// after
juicefs format --update --encrypt-key OLD_KEY meta-url  # key that decrypts stored secrets
# then rotate keys using the documented key-rotation flow
Defensive patterns

Strategy: validation

Validate before calling

keyData, err := os.ReadFile(keyFile)
if err != nil || len(keyData) == 0 {
    return fmt.Errorf("encryption key file missing or empty")
}
// verify key decrypts format before update:
probe := format; if err := probe.Decrypt(); err != nil { /* wrong key */ }

Try / catch

err := format.Update(old)
var keyErr *os.PathError
if err != nil && strings.HasPrefix(err.Error(), "decrypt format") {
    // prompt for the correct original encryption key
}

Prevention

When it happens

Trigger: juicefs format --update or doInit path where f.UUID != old.UUID and f.Decrypt() fails — typically wrong --encrypt-key (AES/SM4) that doesn't match the key used to encrypt the format, or malformed encrypted fields.

Common situations: Rotating encryption keys with the wrong new key supplied; missing or corrupted encrypt key file; copying a format between environments without its key; TestFormat_Update_KeyConflict exercises exactly this conflict path.

Related errors


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