{"record":{"id":"300d69fea5332f81","repo":"siyuan-note/siyuan","slug":"template-destination-already-exists-or-is-inaccess","errorCode":null,"errorMessage":"template destination already exists or is inaccessible","messagePattern":"template destination already exists or is inaccessible","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_manage.go","lineNumber":306,"sourceCode":"\t\t\treturn nil, errors.New(\"cannot write a template directory\")\n\t\t}\n\t\terr = writeTemplateSource(root, request.Path, request.Content, info == nil)\n\t\treturn map[string]string{\"revision\": fmt.Sprintf(\"%x\", sha256.Sum256([]byte(request.Content)))}, err\n\tcase \"move\":\n\t\tif info.IsDir() && isManagedTemplatePackage(root, request.Path) {\n\t\t\treturn nil, errors.New(\"template packages cannot be renamed or moved\")\n\t\t}\n\t\tif err = checkTemplateFilePath(root, request.Target); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = validateNewTemplateName(request.Target); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !info.IsDir() && !strings.EqualFold(path.Ext(request.Target), \".md\") {\n\t\t\treturn nil, errors.New(\"template source must use the .md extension\")\n\t\t}\n\t\tif _, err = root.Stat(request.Target); !errors.Is(err, os.ErrNotExist) {\n\t\t\treturn nil, errors.New(\"template destination already exists or is inaccessible\")\n\t\t}\n\t\treturn nil, root.Rename(request.Path, request.Target)\n\tcase \"remove\":\n\t\t// 将整个目录连同包资源移入隐藏恢复目录，保留误删后的恢复材料。\n\t\ttrash := \".trash/\" + ast.NewNodeID()\n\t\tif err = root.MkdirAll(trash, 0700); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttarget := path.Join(trash, path.Base(request.Path))\n\t\tif err = root.Rename(request.Path, target); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn map[string]string{\"recoveryPath\": target}, nil\n\tdefault:\n\t\treturn nil, errors.New(\"unsupported template operation\")\n\t}\n}\n","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_manage.go#L288-L324","documentation":"ManageTemplateFiles is the kernel API for managing files in the templates workspace (data/templates). When the requested action is a move/rename, the kernel refuses if the destination path already exists on disk or cannot be Stat'ed for another reason (permissions, dangling entry). This guard prevents silently overwriting an existing template file or directory.","triggerScenarios":"Calling ManageTemplateFiles with action=\"move\" (or rename semantics) where request.Target points to a path that already exists inside data/templates, or where root.Stat(request.Target) returns an error other than os.ErrNotExist (e.g. permission problem).","commonSituations":"User or plugin picks a target name that already exists in the template panel; concurrent rename into the same target from two clients; a stale file with odd permissions makes Stat fail; case-insensitive filesystems where the new name differs only in case from an existing template.","solutions":["Pick a unique request.Target name (e.g. append a timestamp or -2 suffix) that does not exist in data/templates","List existing templates first (action=\"list\") and check the target name client-side before issuing the move","Fix filesystem permissions on data/templates so the destination can be stat'ed and inspected","Delete or rename the existing template at the target path first, then retry the move"],"exampleFix":"// before: blind rename, fails when target exists\nawait fetchPost('/api/template/manageTemplateFiles', {action: 'move', path: '/old.md', target: '/tpl.md'});\n// after: ensure target is free\nconst entries = (await fetchPost('/api/template/manageTemplateFiles', {action: 'list'})).data;\nif (!entries.some(e => e.path === '/tpl.md')) {\n  await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path: '/old.md', target: '/tpl.md'});\n}","handlingStrategy":"validation","validationCode":"const list = (await fetchPost('/api/template/manageTemplateFiles', {action: 'list'})).data;\nif (list.some(e => e.path.toLowerCase() === target.toLowerCase())) {\n  throw new Error('target already exists: ' + target);\n}","typeGuard":"const targetIsFree = (entries, target) =>\n  !entries.some(e => e.path === target);","tryCatchPattern":"try {\n  await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path, target});\n} catch (e) {\n  if (String(e.message).includes('destination already exists')) {\n    target = uniqueName(target); // e.g. tpl-2.md\n    await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path, target});\n  } else { throw e; }\n}","preventionTips":["Always list existing templates before choosing a target name","Generate target names with a uniqueness suffix (timestamp or counter)","Treat names case-insensitively to stay safe on case-insensitive filesystems"],"tags":["filesystem","templates","rename-conflict"],"backgroundTag":"file-already-exists","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}