siyuan-note/siyuan · error

task list item marker not found

Error message

task list item marker not found

What it means

Returned when the loaded list-item node has no child of type ast.NodeTaskListItemMarker. The kernel treats ListData.Typ==3 as a task list item, but the actual checkbox marker sub-node (the `[x]`/`[ ]` glyph holder) is missing from the tree — usually indicating data corruption or a hand-edited/malformed .sy file rather than an API misuse.

Source

Thrown at kernel/api/block_op.go:84

		return "", errors.New("block not found")
	}

	if 3 != li.ListData.Typ {
		return "", errors.New("block is not a task list item")
	}

	if 1 != len(marker) {
		return "", errors.New("task list item marker length should be 1")
	}

	liMarker := marker[0]
	if '[' == liMarker || ']' == liMarker {
		return "", errors.New("task list item marker can not be [ or ]")
	}

	markerNode := li.ChildByType(ast.NodeTaskListItemMarker)
	if nil == markerNode {
		return "", errors.New("task list item marker not found")
	}

	markerNode.TaskListItemMarker = liMarker
	markerNode.TaskListItemChecked = ' ' != markerNode.TaskListItemMarker

	treenode.RefreshUpdated(li)

	return luteEngine.RenderNodeBlockDOM(li), nil
}

func updateTaskListItemMarker(c *gin.Context) {
	ret := gulu.Ret.NewResult()
	defer c.JSON(http.StatusOK, ret)

	arg, ok := util.JsonArg(c, ret)
	if !ok {
		return
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Inspect the block's .sy file / use /api/block/getBlockKramdown to confirm the node structure is intact.
  2. If the block is malformed, recreate the task list item in the editor so the kernel regenerates the marker child node.
  3. In programmatic node construction, always append a NodeTaskListItemMarker child when setting ListData.Typ=3 (mirror the test pattern).
  4. Guard callers to skip blocks where ChildByType(NodeTaskListItemMarker) is nil instead of treating them as toggleable.
Defensive patterns

Strategy: type-guard

Validate before calling

// Fetch block kramdown and check it contains a task marker before toggling
const kd = await getBlockKramdown(id);
if (!/\[[ xX]\]/.test(kd)) { throw new Error('block has no task marker child; recreate it'); }

Type guard

// Structural guard: only treat as toggleable when a marker child exists
function isToggleableTaskItem(node): node is TaskItem {
  return node?.ListData?.Typ === 3 && node.children?.some(c => c.Type === 'NodeTaskListItemMarker');
}

Try / catch

try { await updateTaskMarker(id, marker); }
catch (e) { if (/marker not found/.test(e.msg)) { /* recreate block via editor, skip */ } else throw e; }

Prevention

When it happens

Trigger: POST /api/block/updateTaskListItemMarker on a node whose ListData.Typ==3 passed the earlier guard but whose children do not include a NodeTaskListItemMarker. This arises with manually constructed/edited trees, partially migrated blocks, or blocks produced by older versions that did not always emit the marker child.

Common situations: Notebooks imported from Markdown where the importer set Typ=3 but did not synthesize the marker child. Direct .sy file editing or external sync tools that stripped child nodes. Bugs in custom block-generation code that sets ListData.Typ=3 without appending a NodeTaskListItemMarker child (see transaction_list_item_fold_test.go:19 for the correct construction pattern).

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/84fc50895405b91c. Report an issue: GitHub.