{"record":{"id":"d334afd489e44bf6","repo":"siyuan-note/siyuan","slug":"conf-language-25-node-id-localize","errorCode":null,"errorMessage":"Conf.Language(25) + \" [\" + node.ID + \"]\" (localized invalid attribute name message plus block ID)","messagePattern":"Conf\\.Language\\(25\\) \\+ \" \\[\" \\+ node\\.ID \\+ \"\\]\" \\(localized invalid attribute name message plus block ID\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/blockial.go","lineNumber":390,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\toldAttrs = parse.IAL2Map(node.KramdownIAL)\n\tnewAttrsUnEsc := parse.IAL2MapUnEsc(node.KramdownIAL)\n\tfor name := range nameValues {\n\t\tif \"fold\" == strings.ToLower(name) {\n\t\t\tdelete(newAttrsUnEsc, \"heading-fold\")\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor name, value := range nameValues {\n\t\tvalue = util.RemoveInvalidRetainCtrl(value)\n\t\tvalue = strings.TrimSpace(value)\n\t\tlowerName := strings.ToLower(name)\n\t\t// 转换为小写再验证属性名\n\t\tif !isValidAttrName(lowerName) {\n\t\t\terr = errors.New(Conf.Language(25) + \" [\" + node.ID + \"]\")\n\t\t\treturn\n\t\t}\n\t\tif lowerName == \"data-task\" {\n\t\t\terr = errors.New(`setting or removing [data-task] attribute is not allowed via this interface. Please use \"/api/block/updateTaskListItemMarker\" or \"/api/block/batchUpdateTaskListItemMarker\" to update the task list item marker`)\n\t\t\treturn\n\t\t}\n\t\tif DocSortModeAttr == lowerName {\n\t\t\tif ast.NodeDocument != node.Type || IsBoxDoc(boxID, node.ID) {\n\t\t\t\terr = fmt.Errorf(\"attribute [%s] is only supported on regular document roots\", DocSortModeAttr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif \"\" != value {\n\t\t\t\tsortMode, parseErr := strconv.Atoi(value)\n\t\t\t\tif nil != parseErr || !IsValidDocSortMode(sortMode) {\n\t\t\t\t\terr = fmt.Errorf(\"invalid document sort mode [%s]\", value)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tvalue = strconv.Itoa(sortMode)","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/blockial.go#L372-L408","documentation":"setNodeAttrs0 validates every attribute name (lower-cased) with isValidAttrName; if the name contains characters not allowed in a kramdown IAL key, the localized invalid-attribute-name message (key 25) plus the block id in brackets is returned. The whole attribute update is rejected — no partial writes.","triggerScenarios":"Calling setNodeAttrs / BatchSetBlockAttrs / setNodeAttrsWithTx with an attribute key containing invalid characters (spaces, uppercase handling aside, symbols like : \" = or other IAL-breaking characters).","commonSituations":"Plugins building attribute names dynamically from user input; scripts using keys with spaces or punctuation; copying HTML attribute names that contain invalid characters.","solutions":["Sanitize the attribute name: lowercase letters, digits, and hyphens only (custom- prefix recommended)","Rename the offending key before calling the API","Check the block id in the error message to locate which entry in a batch is bad"],"exampleFix":"// before\nsetNodeAttrs(boxID, id, map[string]string{\"my key!\": \"v\"}) // Language(25) + [id]\n// after\nsetNodeAttrs(boxID, id, map[string]string{\"custom-my-key\": \"v\"})","handlingStrategy":"validation","validationCode":"// TS: validate attribute names before sending\nconst validName = /^[a-zA-Z][a-zA-Z0-9-]*$/;\nfor (const k of Object.keys(attrs)) {\n  if (!validName.test(k)) throw new Error(`invalid attr name: ${k}`);\n}","typeGuard":"function hasValidAttrNames(attrs: Record<string,string>): boolean {\n  return Object.keys(attrs).every(k => /^[a-zA-Z][a-zA-Z0-9-]*$/.test(k));\n}","tryCatchPattern":null,"preventionTips":["Lowercase and sanitize attribute names derived from user input","Restrict plugin attribute writes to custom-* keys","Avoid spaces and punctuation in attribute names"],"tags":["attribute-name","validation","ial"],"backgroundTag":"invalid-argument-value","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"}