{"record":{"id":"3a8ae83d150cf7d8","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","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":360,"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\n\t\t// 处理文档标签 https://github.com/siyuan-note/siyuan/issues/13311\n\t\tif lowerName == \"tags\" {\n\t\t\tvar tags []string\n\t\t\ttmp := strings.SplitSeq(value, \",\")\n\t\t\tfor t := range tmp {\n\t\t\t\tt = strings.TrimSpace(t)\n\t\t\t\tif \"\" != t {\n\t\t\t\t\ttags = append(tags, t)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttags = gulu.Str.RemoveDuplicatedElem(tags)","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/blockial.go#L342-L378","documentation":"In setNodeAttrs0, for each requested attribute the name is lower-cased and passed to isValidAttrName; if it fails the function returns errors.New(Conf.Language(25) + ' [' + node.ID + ']') — 'The attribute name can only contain lowercase English letters, digits, and hyphens, and must start with a lowercase English letter'. isValidAttrName (blockial.go:418) requires: non-empty, first char a-z, subsequent chars a-z/0-9/-, with the special 'custom-' prefix case also requiring a lowercase letter after the hyphen.","triggerScenarios":"POST /api/attr/setBlockAttrs (or batchSetBlockAttrs) with an attr key like 'Foo', '1abc', 'name space', 'custom-A', 'custom-' (prefix only), or any key containing underscore/uppercase/non-ASCII. Also via MCP attr tool and the CLI attr command.","commonSituations":"Plugins externalizing an internal field name verbatim (CamelCase or with underscores); user-facing 'key' input that wasn't normalized; migration tooling that copied arbitrary metadata keys.","solutions":["Lower-case and sanitize the key client-side before sending: match ^[a-z][a-z0-9-]*(custom-[a-z][a-z0-9-]*)?$ semantics.","Replace disallowed characters (spaces/underscores) with hyphens and strip the rest.","For user-defined keys, prefer the 'custom-' prefix convention."],"exampleFix":"// before\nattrs := map[string]string{\"Created_At\": \"2024-01-01\"}\n\n// after: normalize the name to the allowed alphabet\nname := strings.ToLower(\"Created_At\")\nname = strings.ReplaceAll(name, \"_\", \"-\")\nattrs := map[string]string{name: \"2024-01-01\"}","handlingStrategy":"validation","validationCode":"// Replicate isValidAttrName before sending.\nfunc validAttrName(name string) bool {\n    name = strings.ToLower(name)\n    if name == \"\" || name[0] < 'a' || name[0] > 'z' { return false }\n    for i := 0; i < len(name); i++ {\n        c := name[i]\n        if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-') { return false }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"// HTTP caller: normalize then retry on this specific error.\nif (r.code === -1 && /attribute name/i.test(r.msg)) {\n    const norm = key.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n    attrs[norm] = attrs[key]; delete attrs[key]\n    r = await fetchSyncPost('/api/attr/setBlockAttrs', {id, attrs})\n}","preventionTips":["Sanitize external field names (lowercase, hyphenate) before using them as IAL keys.","Prefer the 'custom-' prefix convention for user-defined keys.","Reject 'custom-' with no suffix, uppercase, underscores, and whitespace client-side."],"tags":["validation","attrs","ial"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}