kopia/kopia · critical · errInvalidMasterKey
empty key
Error message
empty key
What it means
DeriveKeyFromMasterKey returns this when the supplied master key is empty (len == 0), wrapping the sentinel 'invalid primary key'. HKDF requires non-empty key material, so all purpose-specific derivations (AES keys, auth data, HMAC secrets) are refused early with a clear message instead of a cryptic hkdf failure.
Solutions
- Load/unlock the master key before any derivation call and assert it is non-empty
- Check the key source (env var, config, key file) is actually populated at startup
- Fix the unlock flow so key material is stored in the variable passed to derivation
- In tests, use crypto.TestDeriveKey or generate a key explicitly instead of passing nil
Example fix
// before
masterKey := os.Getenv("KOPIA_MASTER_KEY") // may be ""
derived, err := crypto.DeriveKeyFromMasterKey([]byte(masterKey), salt, purpose, 32)
// after
masterKey := os.Getenv("KOPIA_MASTER_KEY")
if masterKey == "" {
return nil, errors.New("KOPIA_MASTER_KEY is not set")
}
derived, err := crypto.DeriveKeyFromMasterKey([]byte(masterKey), salt, purpose, 32) Defensive patterns
Strategy: validation
Validate before calling
if len(masterKey) == 0 {
return errors.New("master key is empty; load it before deriving subkeys")
} Type guard
func validMasterKey(k []byte) bool { return len(k) > 0 } Try / catch
derived, err := crypto.DeriveKeyFromMasterKey(masterKey, salt, purpose, 32)
if err != nil {
if errors.Is(errors.Cause(err), crypto.ErrInvalidMasterKey) {
return loadAndUnlockKey(ctx)
}
return fmt.Errorf("key derivation failed: %w", err)
} Prevention
- Centralize master-key loading in one function that never returns an empty key
- Check key presence at process startup
- Set and verify key env vars in deployment configs and CI
- Never pass nil keys in tests; use TestDeriveKey or generated keys
When it happens
Trigger: Calling DeriveKeyFromMasterKey (directly or via initCrypto, deriveHMACSecret, DeriveKey) with a nil or zero-length masterKey slice.
Common situations: Repository locked / never unlocked; KOPIA_PASSWORD or key env var not set; reading a key file that is empty or failed to load; unit tests forgetting to provision a test key.
Related errors
- cannot derive AES key
- cannot derive auth data
- cannot initialize cipher
- unable to initialize crypto
- blob id too short
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/b07a57e126d0e175.
Report an issue: GitHub.
Appendix: source
Thrown at internal/crypto/key_derivation.go:15
package crypto
import (
"crypto/hkdf"
"crypto/sha256"
"github.com/pkg/errors"
)
var errInvalidMasterKey = errors.New("invalid primary key")
// DeriveKeyFromMasterKey computes a key for a specific purpose and length using HKDF based on the master key.
func DeriveKeyFromMasterKey(masterKey, salt []byte, purpose string, length int) (derivedKey []byte, err error) {
if len(masterKey) == 0 {
return nil, errors.Wrap(errInvalidMasterKey, "empty key")
}
if derivedKey, err = hkdf.Key(sha256.New, masterKey, salt, purpose, length); err != nil {
return nil, errors.Wrap(err, "unable to derive key")
}
return derivedKey, nil
}
View on GitHub (pinned to 82495e54b5)