siyuan-note/siyuan · error

template destination already exists or is inaccessible

Error message

template destination already exists or is inaccessible

What it means

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.

Source

Thrown at kernel/model/template_manage.go:306

			return nil, errors.New("cannot write a template directory")
		}
		err = writeTemplateSource(root, request.Path, request.Content, info == nil)
		return map[string]string{"revision": fmt.Sprintf("%x", sha256.Sum256([]byte(request.Content)))}, err
	case "move":
		if info.IsDir() && isManagedTemplatePackage(root, request.Path) {
			return nil, errors.New("template packages cannot be renamed or moved")
		}
		if err = checkTemplateFilePath(root, request.Target); err != nil {
			return nil, err
		}
		if err = validateNewTemplateName(request.Target); err != nil {
			return nil, err
		}
		if !info.IsDir() && !strings.EqualFold(path.Ext(request.Target), ".md") {
			return nil, errors.New("template source must use the .md extension")
		}
		if _, err = root.Stat(request.Target); !errors.Is(err, os.ErrNotExist) {
			return nil, errors.New("template destination already exists or is inaccessible")
		}
		return nil, root.Rename(request.Path, request.Target)
	case "remove":
		// 将整个目录连同包资源移入隐藏恢复目录,保留误删后的恢复材料。
		trash := ".trash/" + ast.NewNodeID()
		if err = root.MkdirAll(trash, 0700); err != nil {
			return nil, err
		}
		target := path.Join(trash, path.Base(request.Path))
		if err = root.Rename(request.Path, target); err != nil {
			return nil, err
		}
		return map[string]string{"recoveryPath": target}, nil
	default:
		return nil, errors.New("unsupported template operation")
	}
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Pick a unique request.Target name (e.g. append a timestamp or -2 suffix) that does not exist in data/templates
  2. List existing templates first (action="list") and check the target name client-side before issuing the move
  3. Fix filesystem permissions on data/templates so the destination can be stat'ed and inspected
  4. Delete or rename the existing template at the target path first, then retry the move

Example fix

// before: blind rename, fails when target exists
await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path: '/old.md', target: '/tpl.md'});
// after: ensure target is free
const entries = (await fetchPost('/api/template/manageTemplateFiles', {action: 'list'})).data;
if (!entries.some(e => e.path === '/tpl.md')) {
  await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path: '/old.md', target: '/tpl.md'});
}
Defensive patterns

Strategy: validation

Validate before calling

const list = (await fetchPost('/api/template/manageTemplateFiles', {action: 'list'})).data;
if (list.some(e => e.path.toLowerCase() === target.toLowerCase())) {
  throw new Error('target already exists: ' + target);
}

Type guard

const targetIsFree = (entries, target) =>
  !entries.some(e => e.path === target);

Try / catch

try {
  await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path, target});
} catch (e) {
  if (String(e.message).includes('destination already exists')) {
    target = uniqueName(target); // e.g. tpl-2.md
    await fetchPost('/api/template/manageTemplateFiles', {action: 'move', path, target});
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/300d69fea5332f81. Report an issue: GitHub.