{"record":{"id":"457a3bc8eda9be67","repo":"siyuan-note/siyuan","slug":"the-attribute-name-can-only-contain-lowercase-engl","errorCode":null,"errorMessage":"The attribute name can only contain lowercase English letters, digits, and hyphens, and must start with a lowercase English letter [%s]","messagePattern":"The attribute name can only contain lowercase English letters, digits, and hyphens, and must start with a lowercase English letter \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/blockial.go","lineNumber":387,"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":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/blockial.go#L369-L405","documentation":"Thrown by model.SetBlockAttrs (POST /api/attr/setBlockAttrs) when an attribute name fails isValidAttrName (kernel/model/blockial.go:459) after lowercasing. SiYuan stores block attributes as kramdown IAL keys, so a name must start with a lowercase English letter and contain only lowercase letters, digits, and hyphens; a leading 'c' gets extra 'custom-' prefix handling (the bare prefix 'custom-' alone is rejected). Uppercase input is tolerated because the name is lowercased before validation, but underscores, dots, spaces, and non-ASCII characters are not.","triggerScenarios":"POST /api/attr/setBlockAttrs with a name containing '_', '.', '/', a space, or CJK characters; a name starting with a digit or hyphen; an empty name; or exactly 'custom-'. The rejected block ID is appended to the localized message.","commonSituations":"Scripts that copy HTML data-* names with underscores ('custom_my_field'), generate attribute names from unfiltered user input, or pass camelCase/dotted keys ('user.Score') straight through.","solutions":["Rewrite the name to ^[a-z][a-z0-9-]*$, e.g. 'custom_my_field' -> 'custom-my-field'","Prefix user-defined attributes with 'custom-' following SiYuan's convention for user block attributes","If names are generated dynamically, sanitize them before the call: lowercase, strip invalid characters, ensure the first char is a-z"],"exampleFix":"// before\nawait fetch('/api/attr/setBlockAttrs', {method:'POST', body: JSON.stringify({id, attrs: {'custom_my_field': 'v', 'data-X.Y': 'v'}})});\n\n// after\nawait fetch('/api/attr/setBlockAttrs', {method:'POST', body: JSON.stringify({id, attrs: {'custom-my-field': 'v', 'custom-x-y': 'v'}})});","handlingStrategy":"validation","validationCode":"const attrNameOk = (n) => /^[a-z][a-z0-9-]*$/.test(n) && n !== 'custom-';\nconst attrs = {'custom-my-field': 'v'};\nif (!Object.keys(attrs).every(attrNameOk)) throw new Error('invalid attribute name');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize generated names: lowercase, replace '_' and '.' with '-', ensure the first char is a-z","Keep user-defined attributes under the 'custom-' prefix","Remember names are lowercased server-side; don't rely on case to distinguish two attributes"],"tags":["api","attributes","validation","ial"],"backgroundTag":"attribute-name-validation","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}