{"record":{"id":"19424a69ac8b1dd8","repo":"siyuan-note/siyuan","slug":"s-19424a","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/block.go","lineNumber":651,"sourceCode":"\trelease = func() {}\n\tnotebook, _ := args[\"notebook\"].(string)\n\tnotebook = strings.TrimSpace(notebook)\n\tencrypted := notebook != \"\" && model.IsEncryptedBox(notebook)\n\tif encrypted {\n\t\tmodel.HoldBoxReadLock(notebook)\n\t\tif !model.IsBoxUnlocked(notebook) {\n\t\t\tmodel.ReleaseBoxReadLock(notebook)\n\t\t\treturn \"\", release, fmt.Errorf(\"encrypted notebook is locked, please unlock it first\")\n\t\t}\n\t\trelease = func() {\n\t\t\tmodel.ReleaseBoxReadLock(notebook)\n\t\t}\n\t\tboxID = notebook\n\t}\n\n\tfail := func(format string, values ...any) (string, func(), error) {\n\t\trelease()\n\t\treturn \"\", func() {}, fmt.Errorf(format, values...)\n\t}\n\tfor _, id := range ids {\n\t\tif id == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tqueryBoxID := \"\"\n\t\tif encrypted {\n\t\t\tqueryBoxID = notebook\n\t\t}\n\t\tbt := treenode.GetBlockTreeInExactBox(id, queryBoxID)\n\t\tif bt == nil || (notebook != \"\" && bt.BoxID != notebook) {\n\t\t\tif notebook == \"\" {\n\t\t\t\treturn fail(\"block %s was not found in a normal notebook; provide notebook for encrypted targets\", id)\n\t\t\t}\n\t\t\treturn fail(\"block %s does not belong to notebook %s\", id, notebook)\n\t\t}\n\t\tif mutation && encrypted {\n\t\t\tif treenode.GetBlockTreeInExactBox(id, \"\") != nil {","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/tools/block.go#L633-L669","documentation":"This is the generic failure path of beginBlockToolScope: the internal fail helper releases the held notebook read lock and returns a formatted error with whatever message the surrounding block tool supplied. The message is dynamic (\"%s\"), so the actual text comes from the specific validation that called fail() — typically a per-block-ID check (e.g. block not found) inside the scope setup loop.","triggerScenarios":"Any of the block tools using beginBlockScope fail() during setup: an empty/invalid id resolution, a block ID that cannot be located, or other per-ID validation inside the ids loop of the scope builder.","commonSituations":"MCP callers passing block IDs that do not exist (deleted or from another notebook); IDs with surrounding whitespace that fail lookup; stale IDs cached by an agent after document changes.","solutions":["Read the concrete error text returned after the %s substitution and address the underlying cause (usually a missing/invalid block ID).","Verify the block ID exists (e.g. via SQL or blockGet on a known-good ID) before calling the tool.","Refresh any cached IDs from your client; blocks may have been removed or their documents changed.","Trim/normalize IDs on the caller side to avoid lookup failures."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const known = await queryBlockIDs(); // e.g. via SQL tool\nfor (const id of ids) {\n  if (!/^[0-9]{14}-[a-z0-9]{7}$/.test(id || '')) throw new Error('bad id: ' + id);\n  if (!known.has(id)) throw new Error('unknown block id: ' + id);\n}","typeGuard":"function isBlockID(v) { return typeof v === 'string' && /^[0-9]{14}-[a-z0-9]{7}$/.test(v); }","tryCatchPattern":"try {\n  await mcp.call(\"blockInsert\", { ... });\n} catch (e) {\n  // message is dynamic: log it fully, then validate/re-fetch the offending ID\n  console.error(e.message); // e.g. \"block not found: 2024...-xxx\"\n}","preventionTips":["Always pass trimmed, well-formed SiYuan block IDs","Re-fetch block IDs after any document mutation; never cache across sessions","Log the full dynamic message — it names the concrete underlying problem"],"tags":["mcp","validation","block-lookup"],"backgroundTag":"entity-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}