siyuan-note/siyuan · error

Conf.Language(25) + " [" + node.ID + "]" (localized invalid

Error message

Conf.Language(25) + " [" + node.ID + "]" (localized invalid attribute name message plus block ID)

What it means

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.

Source

Thrown at kernel/model/blockial.go:390

			}
		}
	}
	oldAttrs = parse.IAL2Map(node.KramdownIAL)
	newAttrsUnEsc := parse.IAL2MapUnEsc(node.KramdownIAL)
	for name := range nameValues {
		if "fold" == strings.ToLower(name) {
			delete(newAttrsUnEsc, "heading-fold")
			break
		}
	}

	for name, value := range nameValues {
		value = util.RemoveInvalidRetainCtrl(value)
		value = strings.TrimSpace(value)
		lowerName := strings.ToLower(name)
		// 转换为小写再验证属性名
		if !isValidAttrName(lowerName) {
			err = errors.New(Conf.Language(25) + " [" + node.ID + "]")
			return
		}
		if lowerName == "data-task" {
			err = 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`)
			return
		}
		if DocSortModeAttr == lowerName {
			if ast.NodeDocument != node.Type || IsBoxDoc(boxID, node.ID) {
				err = fmt.Errorf("attribute [%s] is only supported on regular document roots", DocSortModeAttr)
				return
			}
			if "" != value {
				sortMode, parseErr := strconv.Atoi(value)
				if nil != parseErr || !IsValidDocSortMode(sortMode) {
					err = fmt.Errorf("invalid document sort mode [%s]", value)
					return
				}
				value = strconv.Itoa(sortMode)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Sanitize the attribute name: lowercase letters, digits, and hyphens only (custom- prefix recommended)
  2. Rename the offending key before calling the API
  3. Check the block id in the error message to locate which entry in a batch is bad

Example fix

// before
setNodeAttrs(boxID, id, map[string]string{"my key!": "v"}) // Language(25) + [id]
// after
setNodeAttrs(boxID, id, map[string]string{"custom-my-key": "v"})
Defensive patterns

Strategy: validation

Validate before calling

// TS: validate attribute names before sending
const validName = /^[a-zA-Z][a-zA-Z0-9-]*$/;
for (const k of Object.keys(attrs)) {
  if (!validName.test(k)) throw new Error(`invalid attr name: ${k}`);
}

Type guard

function hasValidAttrNames(attrs: Record<string,string>): boolean {
  return Object.keys(attrs).every(k => /^[a-zA-Z][a-zA-Z0-9-]*$/.test(k));
}

Prevention

When it happens

Trigger: Calling setNodeAttrs / BatchSetBlockAttrs / setNodeAttrsWithTx with an attribute key containing invalid characters (spaces, uppercase handling aside, symbols like : " = or other IAL-breaking characters).

Common situations: Plugins building attribute names dynamically from user input; scripts using keys with spaces or punctuation; copying HTML attribute names that contain invalid characters.

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/d334afd489e44bf6. Report an issue: GitHub.