siyuan-note/siyuan · error

Duplicated filename

Error message

Duplicated filename

What it means

The last guard in validateCreateDoc (kernel/model/file.go:2357): box.Exist(p) checks the actual filesystem, and if data/<boxID>/<p> already exists the creation fails with Conf.Language(1) ("Duplicated filename"). Because SiYuan paths embed the document ID, an existing file means the requested ID is already taken — either a genuinely duplicate ID or a stale/orphaned .sy file left behind after a crash or failed index rebuild.

Solutions

  1. Generate a fresh timestamp+random ID for every create attempt (never reuse the previous ID on retry)
  2. If you expect the path to be free, verify first: list the folder (/api/filetree/listDocsByPath) or check the block via /api/block/getBlockInfo
  3. If a stale unindexed .sy file occupies the path, remove it (with the kernel stopped or via file API) and run a rebuild index (/api/filetree/refreshFiletree) before retrying

Example fix

// before
let path = `/20240101120000-fixedid1.sy`
retry(() => fetchPost('/api/filetree/createDocWithMd', {notebook, path, markdown})) // always same path -> duplicate

// after
const newPath = () => `/${Date.now()}...` // fresh ID each attempt
retry(() => fetchPost('/api/filetree/createDocWithMd', {notebook, path: newPath(), markdown}))
Defensive patterns

Strategy: validation

Validate before calling

// Existence pre-check via the kernel's own Box API
if box := Conf.Box(boxID); nil != box && box.Exist(p) {
	return fmt.Errorf("path [%s] already exists, generate a fresh ID", p)
}

Prevention

When it happens

Trigger: POST /api/filetree/createDoc / createDocWithMd reusing a path/ID that already exists (retry logic resubmitting the same generated ID, copy operations deriving paths from the source ID); or an unindexed leftover file at that path after an interrupted delete/create.

Common situations: Naive retry wrappers that regenerate everything except the ID; ID collisions from non-random generators; orphaned .sy files after power loss; docs created in an unopened notebook earlier by file copy.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/a0987af131a39336. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/file.go:2357

		parentID := path.Base(folder)
		parentTree, loadErr := LoadTreeByBlockID(parentID)
		if nil != loadErr {
			logging.LogErrorf("get parent tree [%s] failed", parentID)
			return nil, ErrBlockNotFound
		}
		parentPath := strings.TrimSuffix(parentTree.Path, ".sy")
		if parentTree.Box != boxID || cleanBoxDocDir(parentPath) != cleanBoxDocDir(folder) {
			logging.LogErrorf("parent tree [%s] does not match box [%s] and folder [%s]", parentID, boxID, folder)
			return nil, ErrBlockNotFound
		}
		hPath = path.Join(parentTree.HPath, title)
	}

	if depth := strings.Count(p, "/"); 7 < depth && !Conf.FileTree.AllowCreateDeeper {
		return nil, errors.New(Conf.Language(118))
	}
	if box.Exist(p) {
		return nil, errors.New(Conf.Language(1))
	}

	ret = &createDocValidation{
		box:     box,
		path:    p,
		title:   title,
		hPath:   hPath,
		id:      util.GetTreeID(p),
		folder:  folder,
		isEmpty: isEmpty,
	}
	return
}

func cleanBoxDocDir(p string) string {
	return path.Clean("/" + strings.TrimPrefix(p, "/"))
}

View on GitHub (pinned to afa823b6b4)