siyuan-note/siyuan · error

invalid block ID [ ]

Error message

invalid block ID [%s]

What it means

buildBlockUpdateOperations validates every input's ID against ast.IsNodeIDPattern before parsing its data; an ID that does not match the SiYuan node ID format aborts the whole batch with "invalid block ID [%s]". IDs are updatable-14 style identifiers and the kernel refuses to operate on anything else.

Solutions

  1. Use real IDs returned by the kernel (getBlockInfo, query/sql, editor state) — never generate them client-side.
  2. Validate the ID format before sending (see validation code below).
  3. Trim whitespace and strip quotes/brackets from IDs parsed out of logs or DOM attributes.

Example fix

// before
await updateBlock({ id: '  20240101120000-abc1234  ', data: md });
// after
const id = rawId.trim();
if (!/^[0-9]{14}-[a-z0-9]{7}$/.test(id)) throw new Error(`invalid block id: ${rawId}`);
await updateBlock({ id, data: md });
Defensive patterns

Strategy: validation

Validate before calling

function isValidBlockId(id) {
  return typeof id === 'string' && /^[0-9]{14}-[a-z0-9]{7}$/.test(id.trim());
}

Type guard

function asBlockId(v) {
  return typeof v === 'string' && /^[0-9]{14}-[a-z0-9]{7}$/.test(v.trim()) ? v.trim() : null;
}

Try / catch

try { await updateBlocks(inputs); } catch (e) { if (String(e.msg).includes('invalid block ID')) console.error('fix id source; ids must come from the kernel'); throw e; }

Prevention

When it happens

Trigger: Passing a hand-composed, truncated, whitespace-padded, or empty string as a block ID in a BlockUpdateInput; sending DOM-derived ids that were mangled or placeholder values like "new-block".

Common situations: Scripts generating IDs themselves instead of reading them from the kernel; ids copied with surrounding quotes/whitespace from logs; tests using obviously fake identifiers.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/ca77a9ce1862fcd4. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/block_update.go:93

	transaction := &Transaction{DoOperations: operations}
	if err = performTxSyncLocked(transaction); err != nil {
		return nil, nil, err
	}
	return []*Transaction{transaction}, rootIDs, nil
}

func buildBlockUpdateOperations(inputs []BlockUpdateInput, resolveTree blockUpdateTreeResolver, loadTree blockUpdateTreeLoader) (operations []*Operation, rootIDs []string, err error) {
	if 1 > len(inputs) {
		return nil, nil, errors.New("block updates are empty")
	}

	luteEngine := util.NewLute()
	rootIDSet := map[string]struct{}{}
	treeCache := map[blockUpdateTreeKey]*parse.Tree{}
	for _, input := range inputs {
		if !ast.IsNodeIDPattern(input.ID) {
			return nil, nil, fmt.Errorf("invalid block ID [%s]", input.ID)
		}

		data, dataTree, parseErr := parseBlockUpdateData(input.Data, input.DataType, luteEngine)
		if parseErr != nil {
			return nil, nil, parseErr
		}

		var oldTree *parse.Tree
		var cacheKey blockUpdateTreeKey
		hasCacheKey := false
		if blockTree := resolveTree(input.ID); nil != blockTree {
			cacheKey = blockUpdateTreeKey{boxID: blockTree.BoxID, rootID: blockTree.RootID}
			hasCacheKey = true
			oldTree = treeCache[cacheKey]
		}
		if nil == oldTree {
			var loadErr error
			oldTree, loadErr = loadTree(input.ID)

View on GitHub (pinned to 9f775e8a12)