{"record":{"id":"43a28286f158d1ab","repo":"siyuan-note/siyuan","slug":"conf-language-388-with-escaped-relative-path-htmlstd","errorCode":null,"errorMessage":"Conf.Language(388) with escaped relative path (htmlstd.EscapeString(finalRelPath))","messagePattern":"Conf\\.Language\\(388\\) with escaped relative path \\(htmlstd\\.EscapeString\\(finalRelPath\\)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import.go","lineNumber":951,"sourceCode":"\t\trenderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions, luteEngine.ParseOptions)\n\t\tdata := renderer.Render()\n\n\t\tif !util.UseSingleLineSave {\n\t\t\tbuf := bytes.Buffer{}\n\t\t\tbuf.Grow(1024 * 1024 * 2)\n\t\t\tif err = json.Indent(&buf, data, \"\", \"\\t\"); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdata = buf.Bytes()\n\t\t}\n\n\t\tnewSyPath := filepath.Join(filepath.Dir(syPath), finalSyName)\n\t\tif err = writeImportedTree(boxID, syPath, newSyPath, finalRelPath, data); err != nil {\n\t\t\tlogging.LogErrorf(\"write imported .sy [%s] failed: %s\", syPath, err)\n\t\t\t// 只有\"目标笔记本未解锁导致拒绝写盘\"才替换为提示解锁的文案，其余写盘错误原样上抛，避免归因错误。\n\t\t\t// 相对路径的父目录名来自导入包，需与文档标题一样转义后再进入错误消息。\n\t\t\tif errors.Is(err, errImportedTreeBoxLocked) {\n\t\t\t\terr = errors.New(fmt.Sprintf(Conf.Language(388), htmlstd.EscapeString(finalRelPath)))\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\ttree.Path = finalRelPath\n\t}\n\n\t// 读取包内自定义排序，复制成功后根据目标位置重新编号\n\tsourceSortIDs := map[string]int{}\n\tvar sortErr error\n\tsortPath := filepath.Join(unzipRootPath, \".siyuan\", \"sort.json\")\n\tif filelock.IsExist(sortPath) {\n\t\tsourceSortIDs, sortErr = readSortConfMap(sortPath)\n\t\tif nil != sortErr {\n\t\t\tlogging.LogErrorf(\"read import sort conf failed: %s\", sortErr)\n\t\t\tsourceSortIDs = map[string]int{}\n\t\t}\n\t\tif removeErr := os.RemoveAll(sortPath); nil != removeErr {\n\t\t\tlogging.LogErrorf(\"remove temp sort conf failed: %s\", removeErr)","sourceCodeStart":933,"sourceCodeEnd":969,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/import.go#L933-L969","documentation":"During .sy import, each document tree is written into the target notebook via writeImportedTree. If writing fails specifically because the target notebook is locked/encrypted and currently not unlocked (errImportedTreeBoxLocked), the error is replaced with the localized message for language key 388, which embeds the document's relative path — HTML-escaped first since that path comes from the imported package. Any other write error is propagated unchanged to avoid mis-attribution.","triggerScenarios":"Importing a .sy/.sy.zip package into an encrypted notebook whose lease is not unlocked: writeImportedTree rejects the write with errImportedTreeBoxLocked, and importSY0 converts it to the unlock prompt containing the escaped relative path of the document being written.","commonSituations":"1) Attempting an import into an encrypted notebook while its unlock session has expired. 2) Restarting the kernel between unlocking and importing. 3) Automating imports against an encrypted notebook without an unlock call first.","solutions":["Unlock the target encrypted notebook (enter its passphrase via the unlock dialog/API) to obtain a decryption lease, then retry the import.","If the notebook should not be encrypted, decrypt/remove encryption on the target notebook before importing.","If the write error is not lock-related, inspect the original error logged by logging.LogErrorf (the kernel log) for the real cause (disk full, permissions).","Import into a different, unencrypted notebook or create a new notebook (createNotebook=true) instead."],"exampleFix":"// before\nfetch('/api/import/sy', {body: JSON.stringify({notebook: boxID, src: 'pkg.zip'})}) // notebook locked\n// after\nawait unlockNotebook(boxID, passphrase) // obtain lease first\nfetch('/api/import/sy', {body: JSON.stringify({notebook: boxID, src: 'pkg.zip'})})","handlingStrategy":"try-catch","validationCode":"const box = await getNotebook(boxID);\nif (box.closed) { await openNotebook(boxID); }\n// if the notebook is encrypted, ensure an unlock lease exists before importing","typeGuard":null,"tryCatchPattern":"try {\n  await importSY(boxID, src);\n} catch (e) {\n  if (String(e.msg).includes('388') || /解锁|unlock/i.test(e.msg)) {\n    await unlockNotebook(boxID, passphrase);\n    return importSY(boxID, src); // retry once after unlocking\n  }\n  throw e;\n}","preventionTips":["Unlock encrypted notebooks before batch imports and keep the session alive","Re-unlock after kernel restarts — the lease does not survive reboot","Prefer importing into an unencrypted staging notebook for automation"],"tags":["import","encryption","file-write","siyuan"],"backgroundTag":"file-write-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}