{"record":{"id":"ca77a9ce1862fcd4","repo":"siyuan-note/siyuan","slug":"invalid-block-id-s","errorCode":null,"errorMessage":"invalid block ID [%s]","messagePattern":"invalid block ID \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/block_update.go","lineNumber":93,"sourceCode":"\n\ttransaction := &Transaction{DoOperations: operations}\n\tif err = performTxSyncLocked(transaction); err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn []*Transaction{transaction}, rootIDs, nil\n}\n\nfunc buildBlockUpdateOperations(inputs []BlockUpdateInput, resolveTree blockUpdateTreeResolver, loadTree blockUpdateTreeLoader) (operations []*Operation, rootIDs []string, err error) {\n\tif 1 > len(inputs) {\n\t\treturn nil, nil, errors.New(\"block updates are empty\")\n\t}\n\n\tluteEngine := util.NewLute()\n\trootIDSet := map[string]struct{}{}\n\ttreeCache := map[blockUpdateTreeKey]*parse.Tree{}\n\tfor _, input := range inputs {\n\t\tif !ast.IsNodeIDPattern(input.ID) {\n\t\t\treturn nil, nil, fmt.Errorf(\"invalid block ID [%s]\", input.ID)\n\t\t}\n\n\t\tdata, dataTree, parseErr := parseBlockUpdateData(input.Data, input.DataType, luteEngine)\n\t\tif parseErr != nil {\n\t\t\treturn nil, nil, parseErr\n\t\t}\n\n\t\tvar oldTree *parse.Tree\n\t\tvar cacheKey blockUpdateTreeKey\n\t\thasCacheKey := false\n\t\tif blockTree := resolveTree(input.ID); nil != blockTree {\n\t\t\tcacheKey = blockUpdateTreeKey{boxID: blockTree.BoxID, rootID: blockTree.RootID}\n\t\t\thasCacheKey = true\n\t\t\toldTree = treeCache[cacheKey]\n\t\t}\n\t\tif nil == oldTree {\n\t\t\tvar loadErr error\n\t\t\toldTree, loadErr = loadTree(input.ID)","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/block_update.go#L75-L111","documentation":"Returned by buildBlockUpdateOperations for any input whose ID fails ast.IsNodeIDPattern. SiYuan block IDs follow a fixed pattern (20-char ASCII alphanumeric); this guard rejects malformed, empty, or non-block identifiers before any tree work happens.","triggerScenarios":"Passing a path, URL, or document ID instead of a block ID; passing an empty string; truncated or whitespace-padded ID; ID generated by something other than the SiYuan ID generator.","commonSituations":"Client confusing docID/rootID with blockID; copy-paste introducing whitespace; external integration using its own ID scheme.","solutions":["Run ast.IsNodeIDPattern(id) on every input before calling the API and drop invalid entries.","Trim whitespace and verify the ID length matches the expected block ID length.","Confirm the ID comes from a blocks table query, not from a different namespace."],"exampleFix":"// before\nfor _, in := range inputs {\n    buildBlockUpdateOperations(...)\n}\n\n// after\nfor _, in := range inputs {\n    if !ast.IsNodeIDPattern(in.ID) {\n        return fmt.Errorf(\"skip invalid id %q\", in.ID)\n    }\n}","handlingStrategy":"validation","validationCode":"for _, in := range inputs {\n    if !ast.IsNodeIDPattern(in.ID) {\n        return fmt.Errorf(\"invalid block ID %q\", in.ID)\n    }\n}","typeGuard":"func isValidBlockID(id string) bool {\n    return ast.IsNodeIDPattern(id)\n}","tryCatchPattern":null,"preventionTips":["Source block IDs only from the blocks table or editor selection, never hand-written.","Trim whitespace from copied IDs before submission.","Run IsNodeIDPattern on every ID before calling the update API."],"tags":["block-update","validation","block-id"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}