siyuan-note/siyuan · error
Conf.Language(112), invalidChar
Error message
Conf.Language(112), invalidChar
What it means
RenameTag validates the new tag label with treenode.ContainsMarker before performing the rename. ContainsMarker (kernel/treenode/marker.go:24) rejects ASCII strings containing marker characters (characters that have special meaning in Markdown/attribute syntax), and RenameTag returns fmt.Errorf(Conf.Language(112), invalidChar) — a localized message with the offending character interpolated. The error means the requested new tag label contains a reserved marker character and cannot be used.
Solutions
- Remove the marker character (e.g. '#', '/', '[]') from the new label and retry the rename.
- Strip leading/trailing '/' and whitespace yourself before calling, since the marker check runs before trimming.
- Sanitize programmatically: filter out ASCII marker characters from the label before invoking RenameTag.
- Use the localized message text (Language(112)) which names the offending character to guide the user.
Example fix
// before: raw user input passed straight to the API
await fetchPost("/api/format/renameTag", {oldLabel: oldTag, newLabel: input});
// after: reject marker characters up front
const markerChars = /[\x20-\x7e]/; // combined with the kernel's marker set
if (/[#\/[\]()!`*~_>-]/.test(input)) {
showMessage("The new tag name contains an invalid character (e.g. # or /)");
} else {
await fetchPost("/api/format/renameTag", {oldLabel: oldTag, newLabel: input.trim().replace(/^\/|\/$/g, "")});
} Defensive patterns
Strategy: validation
Validate before calling
// reject marker characters in the new tag label before calling the API
function isValidTagLabel(label) {
if (!/^\x00-\x7f*$/.test(label)) return true; // non-ASCII bypasses kernel check
return !/[#\/[\]()!`*~_{}>'"~@+$%^&|=;:,?<\\ -]/.test(label.replace(/^\/|\/$/g, "").trim());
}
if (!isValidTagLabel(newLabel)) throw new Error("tag name contains an invalid marker character"); Type guard
function hasMarkerChar(label) {
if (!label || !/^[-\x7f]*$/.test(label)) return false; // non-ASCII is allowed by the kernel
const markers = "#/[]()!`*~_-{}>\"'"; // mirror treenode.IsMarker set
return [...label].some(c => markers.includes(c));
} Try / catch
try {
await fetchPost("/api/format/renameTag", {oldLabel, newLabel});
} catch (e) {
showMessage(e.message); // localized: names the offending character
} Prevention
- Sanitize tag labels (strip leading/trailing '/' and whitespace) before renaming
- Do not generate tags from raw Markdown/heading text containing #, brackets, or slashes
- Validate input in the tag-rename dialog before submitting
- In batch renames, pre-filter labels with the kernel's marker-character rules
When it happens
Trigger: Calling RenameTag (API /api/format/tagRenameContract, or the tag panel rename dialog) with newLabel containing an ASCII marker character such as #, /, [, ], (, ), or other IsMarker characters. Non-ASCII input bypasses the check (ContainsMarker returns "" for non-ASCII strings), so this fires specifically for ASCII labels with marker chars. Note the check runs on the raw newLabel, before trimming surrounding '/' and whitespace.
Common situations: User pastes a Markdown heading like '#todo' or a hierarchical path '/work/project/' intending slash hierarchy; scripts generating tags from file names or issue titles containing brackets or parentheses; batch-rename automation that doesn't sanitize labels.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- all asset mappings failed
- assetPath is not an image referenced by the document
- assetPath is required for analyze
- at least one asset path or mapping is required
- binary files cannot be edited as text
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/30493412f29f2727.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/tag.go:128
indexHistoryDir(filepath.Base(historyDir), util.NewLute())
sql.FlushQueue()
reloadTreeIDs = gulu.Str.RemoveDuplicatedElem(reloadTreeIDs)
for _, id := range reloadTreeIDs {
ReloadProtyle(id)
}
updateAttributeViewBlockText(updateNodes)
sql.FlushQueue()
util.PushClearProgress()
return
}
func RenameTag(oldLabel, newLabel string) (err error) {
if invalidChar := treenode.ContainsMarker(newLabel); "" != invalidChar {
return fmt.Errorf(Conf.Language(112), invalidChar)
}
newLabel = strings.TrimPrefix(newLabel, "/")
newLabel = strings.TrimSuffix(newLabel, "/")
newLabel = strings.TrimSpace(newLabel)
if "" == newLabel {
return errors.New(Conf.Language(114))
}
if oldLabel == newLabel {
return
}
util.PushEndlessProgress(Conf.Language(110))
util.RandomSleep(500, 1000)
tags := sql.QueryTagSpansByLabel(oldLabel)View on GitHub (pinned to 9f775e8a12)