{"record":{"id":"1f94c7e08043e391","repo":"siyuan-note/siyuan","slug":"bookmark-cannot-be-empty","errorCode":null,"errorMessage":"Bookmark cannot be empty","messagePattern":"Bookmark cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/bookmark.go","lineNumber":103,"sourceCode":"\n\t\tutil.RandomSleep(50, 150)\n\t}\n\n\tindexHistoryDir(filepath.Base(historyDir), util.NewLute())\n\tsql.FlushQueue()\n\n\tutil.ReloadUI()\n\treturn\n}\n\nfunc RenameBookmark(oldBookmark, newBookmark string) (err error) {\n\tif invalidChar := treenode.ContainsMarker(newBookmark); \"\" != invalidChar {\n\t\treturn fmt.Errorf(Conf.Language(112), invalidChar)\n\t}\n\n\tnewBookmark = strings.TrimSpace(newBookmark)\n\tif \"\" == newBookmark {\n\t\treturn errors.New(Conf.Language(126))\n\t}\n\n\tif oldBookmark == newBookmark {\n\t\treturn\n\t}\n\n\tutil.PushEndlessProgress(Conf.Language(110))\n\tdefer util.ClearPushProgress(100)\n\n\tbookmarks := sql.QueryBookmarkBlocks()\n\ttreeBlocks := map[string][]string{}\n\tfor _, bm := range bookmarks {\n\t\tif blocks, ok := treeBlocks[bm.RootID]; !ok {\n\t\t\ttreeBlocks[bm.RootID] = []string{bm.ID}\n\t\t} else {\n\t\t\ttreeBlocks[bm.RootID] = append(blocks, bm.ID)\n\t\t}\n\t}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/bookmark.go#L85-L121","documentation":"In RenameBookmark, after the marker check (error 529) passes, the name is TrimSpace'd and if empty the function returns errors.New(Conf.Language(126)) — 'Bookmark cannot be empty'. Note the ordering: a name that consists only of whitespace plus a marker character hits error 529 first; only whitespace/marker-free emptiness reaches this check. A no-op rename (old == new) returns nil after this.","triggerScenarios":"POST /api/bookmark/renameBookmark with newBookmark being empty, only spaces, or only tabs/newlines (and containing no Markdown marker chars).","commonSituations":"UI submitting an empty input field; programmatic caller passing '' to 'clear' a bookmark (renaming-to-empty is not how you remove one); trimming user input down to nothing.","solutions":["To delete a bookmark, use the remove/disband bookmark flow, not an empty rename.","Validate newBookmark is non-empty after TrimSpace on the client before calling.","Disable the rename confirm button while the input is blank."],"exampleFix":"// before\nfetchPost('/api/bookmark/renameBookmark', {oldBookmark, newBookmark: ''})\n\n// after: reject empty up front; remove via the proper flow when intended\nconst name = newBookmark.trim()\nif (!name) { showMessage(window.siyuan.languages['_kernel'][126]); return }\nfetchPost('/api/bookmark/renameBookmark', {oldBookmark, newBookmark: name})","handlingStrategy":"validation","validationCode":"// Reject empty (marker-free) names on the client; use the remove flow to delete.\nname := strings.TrimSpace(newBookmark)\nif name == \"\" {\n    return errors.New(\"bookmark name required; use remove bookmark to delete\")\n}","typeGuard":null,"tryCatchPattern":"// HTTP caller: disable submit while the trimmed input is empty.\nif (!newBookmark.trim()) { showMessage(emptyMsg); return }","preventionTips":["Provide a separate 'remove bookmark' action rather than rename-to-empty.","Disable the confirm button while the input is blank."],"tags":["bookmark","validation"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}