{"record":{"id":"d70b8b7912c59283","repo":"siyuan-note/siyuan","slug":"block-s-is-not-a-document","errorCode":null,"errorMessage":"block [%s] is not a document","messagePattern":"block \\[(.+?)\\] is not a document","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/blockinfo.go","lineNumber":596,"sourceCode":"\n\tfor _, id := range ids {\n\t\tret[id] = nodesIndexes[id]\n\t}\n\treturn\n}\n\nfunc GetDocBlocksOrders(id string) (ret []string, err error) {\n\tret = []string{}\n\ttree, err := LoadTreeByBlockID(id)\n\tif err != nil {\n\t\treturn\n\t}\n\tif nil == tree || nil == tree.Root {\n\t\terr = ErrTreeNotFound\n\t\treturn\n\t}\n\tif tree.Root.ID != id {\n\t\terr = fmt.Errorf(\"block [%s] is not a document\", id)\n\t\treturn\n\t}\n\n\tret = getDocBlocksOrdersInTree(tree)\n\treturn\n}\n\nfunc getDocBlocksOrdersInTree(tree *parse.Tree) (ret []string) {\n\tret = []string{}\n\tast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {\n\t\tif !entering || n == tree.Root || !n.IsBlock() || ast.NodeKramdownBlockIAL == n.Type || \"\" == n.ID {\n\t\t\treturn ast.WalkContinue\n\t\t}\n\n\t\tret = append(ret, n.ID)\n\t\treturn ast.WalkContinue\n\t})\n\treturn","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/blockinfo.go#L578-L614","documentation":"GetDocBlocksOrders collects the block ordering structure of a document tree, but it first verifies that the given ID actually belongs to the tree's root document block. If the tree loaded for `id` has a root whose ID differs from the requested ID, the kernel rejects the call because only a document (root) ID is valid here, not a child block or arbitrary ID.","triggerScenarios":"Calling GetDocBlocksOrders (or the API surface that wraps it) with: (1) a child-block ID instead of the document ID; (2) a stale/deleted document ID whose tree resolution falls back to a different root; (3) a mistyped or truncated block ID.","commonSituations":"Plugins or scripts iterate the SQL blocks table and pass non-root block IDs; code caches document IDs from a previous session that no longer match after re-index or sync; user-supplied IDs from links contain an anchor block ID rather than the doc ID.","solutions":["Pass the document's root ID (tree.Root.ID), not a child block ID — e.g. query `SELECT root_id FROM blocks WHERE id = ?` first","Verify the document still exists and matches the ID (check for sync/renames that changed the root ID)","Trim/validate the ID string for typos or truncation before calling","Handle the error by falling back to resolving the parent document of the block instead of failing"],"exampleFix":"// before\norders, err := model.GetDocBlocksOrders(blockID) // blockID may be a child\n// after\nvar rootID string\nrow := db.QueryRow(\"SELECT root_id FROM blocks WHERE id = ?\", blockID)\nrow.Scan(&rootID)\norders, err := model.GetDocBlocksOrders(rootID)","handlingStrategy":"validation","validationCode":"function isDocID(id string, db *sql.DB) bool {\n  var rootID string\n  return db.QueryRow(\"SELECT root_id FROM blocks WHERE id = ?\", id).Scan(&rootID) == nil && rootID == id\n}","typeGuard":null,"tryCatchPattern":"orders, err := model.GetDocBlocksOrders(id)\nif err != nil && strings.Contains(err.Error(), \"is not a document\") {\n  // resolve parent document and retry\n}","preventionTips":["Always pass tree.Root.ID / blocks.root_id, never a child block ID","Validate IDs exist and are roots before calling document-level APIs","Refresh cached doc IDs after sync or reindex"],"tags":["go","kernel","document","block-id"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}