{"record":{"id":"0d8cb4f281843567","repo":"siyuan-note/siyuan","slug":"current-document-id-is-invalid","errorCode":null,"errorMessage":"current document ID is invalid","messagePattern":"current document ID is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history_diff.go","lineNumber":122,"sourceCode":"\tstart      int\n\tend        int\n\tstoredRuns []string\n\tsignature  string\n}\n\ntype docDiffLCSBudget struct {\n\tremaining int\n}\n\n// ResolveDocVersionBoxID 返回文档版本引用中明确记录的加密笔记本 ID。\nfunc ResolveDocVersionBoxID(ref *DocVersionRef) (string, error) {\n\tif ref == nil {\n\t\treturn \"\", errors.New(\"document version is required\")\n\t}\n\tswitch ref.Type {\n\tcase docVersionCurrent:\n\t\tif !ast.IsNodeIDPattern(ref.ID) {\n\t\t\treturn \"\", errors.New(\"current document ID is invalid\")\n\t\t}\n\t\tblockTree := treenode.GetBlockTree(ref.ID)\n\t\tif blockTree == nil {\n\t\t\treturn \"\", ErrTreeNotFound\n\t\t}\n\t\tif IsEncryptedBox(blockTree.BoxID) {\n\t\t\treturn blockTree.BoxID, nil\n\t\t}\n\t\treturn \"\", nil\n\tcase docVersionHistory:\n\t\tabsPath, err := validateHistoryPath(ref.Path)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tboxID := ExtractBoxIDFromHistoryPath(absPath)\n\t\tif IsEncryptedBox(boxID) {\n\t\t\treturn boxID, nil\n\t\t}","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/history_diff.go#L104-L140","documentation":"For a docVersionCurrent reference, ResolveDocVersionBoxID validates that ref.ID looks like a node ID (ast.IsNodeIDPattern) before looking it up in the block tree. If the ID string is not a well-formed node ID, this error is returned; the reference is malformed, not merely unknown.","triggerScenarios":"ResolveDocVersionBoxID receives a DocVersionRef with Type == docVersionCurrent and ref.ID that is empty, truncated, URL-damaged, or not a node-ID-shaped string (e.g. a file path or human title passed instead of a block ID).","commonSituations":"Client passed a document path instead of its ID; ID truncated by string manipulation; copy-paste dropped characters; plugin constructed the ref manually with the wrong field; query param URL-decoding mangled the ID.","solutions":["Pass the document's root block ID (from GetDocID / block tree) in DocVersionRef.ID.","Validate the ID client-side with a node-ID pattern check (same rule as ast.IsNodeIDPattern: length-20 [0-9a-z]{20}-style ID) before calling.","If the value came from a URL, check encoding/decoding did not corrupt it.","Log the offending ref.ID and correct the producing code path."],"exampleFix":"// before\nref := &model.DocVersionRef{Type: model.DocVersionCurrent, ID: docPath}\n// after\nref := &model.DocVersionRef{Type: model.DocVersionCurrent, ID: docRootID /* 20-char node ID */}","handlingStrategy":"validation","validationCode":"var nodeIDPattern = regexp.MustCompile(`^\\d{14}-[0-9a-z]{6}$`)\nif !nodeIDPattern.MatchString(ref.ID) {\n    return fmt.Errorf(\"%q is not a valid node ID\", ref.ID)\n}\nboxID, err := model.ResolveDocVersionBoxID(ref)","typeGuard":"func isNodeIDLike(s string) bool { return len(s) == 21 && s[14] == '-' }","tryCatchPattern":"boxID, err := model.ResolveDocVersionBoxID(ref)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid\") {\n        return fmt.Errorf(\"bad ref.ID %q: %w\", ref.ID, err)\n    }\n    return err\n}","preventionTips":["Always source IDs from kernel APIs, never from file paths or titles","URL-encode IDs in query strings","Validate ID shape before calling version APIs"],"tags":["validation","identifier","document-history"],"backgroundTag":"invalid-identifier-format","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"}