AlistGo/alist · error
failed to obfuscate salt: %w
Error message
failed to obfuscate salt: %w
What it means
Initialization error in the Crypt driver, identical in mechanism to the password case: Init calls updateObfusParm on the Salt field, which runs rclone's obscure.Obscure on the plaintext value. rclone rejects obscuring an empty string, so an empty (non-obfuscated-prefixed) salt makes Init fail with this wrapped message before the storage becomes usable.
Source
Thrown at drivers/crypt/driver.go:52
const obfuscatedPrefix = "___Obfuscated___"
func (d *Crypt) Config() driver.Config {
return config
}
func (d *Crypt) GetAddition() driver.Additional {
return &d.Addition
}
func (d *Crypt) Init(ctx context.Context) error {
//obfuscate credentials if it's updated or just created
err := d.updateObfusParm(&d.Password)
if err != nil {
return fmt.Errorf("failed to obfuscate password: %w", err)
}
err = d.updateObfusParm(&d.Salt)
if err != nil {
return fmt.Errorf("failed to obfuscate salt: %w", err)
}
isCryptExt := regexp.MustCompile(`^[.][A-Za-z0-9-_]{2,}$`).MatchString
if !isCryptExt(d.EncryptedSuffix) {
return fmt.Errorf("EncryptedSuffix is Illegal")
}
d.FileNameEncoding = utils.GetNoneEmpty(d.FileNameEncoding, "base64")
d.EncryptedSuffix = utils.GetNoneEmpty(d.EncryptedSuffix, ".bin")
op.MustSaveDriverStorage(d)
//need remote storage exist
storage, err := fs.GetStorage(d.RemotePath, &fs.GetStoragesArgs{})
if err != nil {
return fmt.Errorf("can't find remote storage: %w", err)
}
d.remoteStorage = storage
View on GitHub (pinned to 843d9dc814)
Solutions
- Set a non-empty salt (a random string is typical; it is not secret, only needs to be stable so derived name encryption stays consistent)
- Keep the salt identical across re-initializations, or encrypted filenames from before will become undecryptable
- Once set, it is stored obfuscated and later Inits skip the failing branch
Example fix
// before
{
"password": "pass",
"salt": ""
}
// after
{
"password": "pass",
"salt": "aStableSaltValue"
} Defensive patterns
Strategy: validation
Validate before calling
// before saving: salt must be non-empty (or already obfuscated)
if !strings.HasPrefix(cfg.Salt, "___Obfuscated___") && strings.TrimSpace(cfg.Salt) == "" {
return errors.New("crypt storage requires a non-empty salt")
} Type guard
func hasUsableCryptSalt(v string) bool {
return strings.HasPrefix(v, "___Obfuscated___") || strings.TrimSpace(v) != ""
} Try / catch
if err := cryptStorage.Init(ctx); err != nil {
if strings.Contains(err.Error(), "failed to obfuscate salt") {
return errors.New("crypt salt is empty; set a stable non-empty salt")
}
} Prevention
- Generate a stable random salt at storage-creation time in wrappers
- Never change the salt after files are encrypted — filenames become undecryptable
- Treat the salt as required configuration, not optional
When it happens
Trigger: Creating or updating a Crypt storage with an empty salt field; the salt does not already start with ___Obfuscated___, so the obfuscation path runs and fails on empty input.
Common situations: Optional-looking salt field left blank during setup (note: unlike the suffix fields, no default is filled in before this call); clearing the salt during reconfiguration; scripted provisioning that writes an empty salt.
Related errors
- failed to obfuscate password: %w
- EncryptedSuffix is Illegal
- unsupported meta_format: %s
- unsupported hash_type: %s
- hash_type %q requires meta_format=simplejson
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/12bf64c2eb544e5a.
Report an issue: GitHub.