{"record":{"id":"12bf64c2eb544e5a","repo":"AlistGo/alist","slug":"failed-to-obfuscate-salt-w","errorCode":null,"errorMessage":"failed to obfuscate salt: %w","messagePattern":"failed to obfuscate salt: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/crypt/driver.go","lineNumber":52,"sourceCode":"const obfuscatedPrefix = \"___Obfuscated___\"\n\nfunc (d *Crypt) Config() driver.Config {\n\treturn config\n}\n\nfunc (d *Crypt) GetAddition() driver.Additional {\n\treturn &d.Addition\n}\n\nfunc (d *Crypt) Init(ctx context.Context) error {\n\t//obfuscate credentials if it's updated or just created\n\terr := d.updateObfusParm(&d.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to obfuscate password: %w\", err)\n\t}\n\terr = d.updateObfusParm(&d.Salt)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to obfuscate salt: %w\", err)\n\t}\n\n\tisCryptExt := regexp.MustCompile(`^[.][A-Za-z0-9-_]{2,}$`).MatchString\n\tif !isCryptExt(d.EncryptedSuffix) {\n\t\treturn fmt.Errorf(\"EncryptedSuffix is Illegal\")\n\t}\n\td.FileNameEncoding = utils.GetNoneEmpty(d.FileNameEncoding, \"base64\")\n\td.EncryptedSuffix = utils.GetNoneEmpty(d.EncryptedSuffix, \".bin\")\n\n\top.MustSaveDriverStorage(d)\n\n\t//need remote storage exist\n\tstorage, err := fs.GetStorage(d.RemotePath, &fs.GetStoragesArgs{})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't find remote storage: %w\", err)\n\t}\n\td.remoteStorage = storage\n","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/crypt/driver.go#L34-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n{\n  \"password\": \"pass\",\n  \"salt\": \"\"\n}\n\n// after\n{\n  \"password\": \"pass\",\n  \"salt\": \"aStableSaltValue\"\n}","handlingStrategy":"validation","validationCode":"// before saving: salt must be non-empty (or already obfuscated)\nif !strings.HasPrefix(cfg.Salt, \"___Obfuscated___\") && strings.TrimSpace(cfg.Salt) == \"\" {\n    return errors.New(\"crypt storage requires a non-empty salt\")\n}","typeGuard":"func hasUsableCryptSalt(v string) bool {\n    return strings.HasPrefix(v, \"___Obfuscated___\") || strings.TrimSpace(v) != \"\"\n}","tryCatchPattern":"if err := cryptStorage.Init(ctx); err != nil {\n    if strings.Contains(err.Error(), \"failed to obfuscate salt\") {\n        return errors.New(\"crypt salt is empty; set a stable non-empty salt\")\n    }\n}","preventionTips":["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"],"tags":["alist","crypt","configuration","salt","init"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}