{"record":{"id":"f3d683e683dd3eec","repo":"siyuan-note/siyuan","slug":"please-unlock-the-encrypted-notebook-first-f3d683","errorCode":null,"errorMessage":"Please unlock the encrypted notebook first","messagePattern":"Please unlock the encrypted notebook first","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import.go","lineNumber":1019,"sourceCode":"\t\tif err = filelock.Copy(assetsDir, dataAssets); err != nil {\n\t\t\tlogging.LogErrorf(\"copy assets from [%s] to [%s] failed: %s\", assetsDir, dataAssets, err)\n\t\t\treturn nil, err\n\t\t}\n\t\tif removeErr := os.RemoveAll(assetsDir); removeErr != nil {\n\t\t\treturn nil, removeErr\n\t\t}\n\t}\n\treturn assetPathMap, nil\n}\n\nfunc writeImportedTree(boxID, syPath, newSyPath, relPath string, data []byte) error {\n\tif IsEncryptedBox(boxID) {\n\t\tHoldBoxReadLock(boxID)\n\t\tdefer ReleaseBoxReadLock(boxID)\n\n\t\tdek, err := GetDEKIfUnlocked(boxID)\n\t\tif err != nil {\n\t\t\treturn errors.New(Conf.Language(314))\n\t\t}\n\t\tdata, err = EncryptFile(boxID, relPath, dek, data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := os.WriteFile(syPath, data, 0644); err != nil {\n\t\treturn err\n\t}\n\treturn filelock.Rename(syPath, newSyPath)\n}\n\nfunc validateImportedNotebookIdentities(tmpDataPath string) ([]string, error) {\n\tdirs, err := os.ReadDir(tmpDataPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":1001,"sourceCodeEnd":1037,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import.go#L1001-L1037","documentation":"Thrown by writeImportedTree when the target notebook is encrypted but not currently unlocked. The function calls GetDEKIfUnlocked(boxID) to retrieve the Data Encryption Key (DEK) needed to encrypt the imported tree before writing it to disk. If the notebook is locked (no DEK cached, or the box is not in an unlocked state), the DEK retrieval fails and the write is aborted. The error message uses language code 314.","triggerScenarios":"Calling writeImportedTree (internally from ImportSYZip or ImportData) for a boxID where IsEncryptedBox returns true but isBoxUnlockedForAccess returns false. This happens when the encrypted notebook exists but the user has not entered the master password in the current session, or the notebook was locked/unmounted after unlocking.","commonSituations":"Importing content into an encrypted notebook after a kernel restart without re-unlocking. Notebook was auto-locked due to idle timeout. Importing via API or automation without first calling the unlock endpoint. Race condition where the notebook is locked between the start of import and the write step.","solutions":["Unlock the encrypted notebook before importing: call the unlock API or enter the master password in the UI.","Verify the notebook is in the unlocked state by checking IsEncryptedBox and isBoxUnlockedForAccess before calling import functions.","If importing via API, ensure the unlock endpoint is called first and the session/token maintains the unlocked state.","Import into a non-encrypted notebook if the encrypted one cannot be unlocked (e.g., master password forgotten)."],"exampleFix":"// before: import without checking unlock state\nerr := writeImportedTree(boxID, syPath, newSyPath, relPath, data)\n\n// after: verify unlocked state before writing\nif model.IsEncryptedBox(boxID) {\n    if _, err := model.GetDEKIfUnlocked(boxID); err != nil {\n        return fmt.Errorf(\"notebook %s must be unlocked before import\", boxID)\n    }\n}\nerr := writeImportedTree(boxID, syPath, newSyPath, relPath, data)","handlingStrategy":"validation","validationCode":"// Before importing into an encrypted notebook, verify it is unlocked\nfunc ensureBoxUnlocked(boxID string) error {\n    if !model.IsEncryptedBox(boxID) {\n        return nil // not encrypted, no check needed\n    }\n    if _, err := model.GetDEKIfUnlocked(boxID); err != nil {\n        return fmt.Errorf(\"encrypted notebook %s must be unlocked before import: %w\", boxID, err)\n    }\n    return nil\n}\n\n// Call before any import function:\nif err := ensureBoxUnlocked(boxID); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"// In writeImportedTree caller, handle the locked-notebook error\nerr := writeImportedTree(boxID, syPath, newSyPath, relPath, data)\nif err != nil {\n    if err.Error() == Conf.Language(314) {\n        // Prompt user to unlock, or skip this file\n        util.PushMsg(Conf.Language(314))\n        return err\n    }\n}","preventionTips":["Always call the unlock API or enter the master password before importing into encrypted notebooks.","Check IsEncryptedBox and GetDEKIfUnlocked before calling import functions.","In batch import workflows, verify the unlock state once at the start and re-check if the operation is long-running.","Handle notebook auto-lock timeouts by re-unlocking if the import takes longer than the idle timeout."],"tags":["encryption","import","locked","dek","sy-zip"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}