{"record":{"id":"ec7af382c45d42ab","repo":"siyuan-note/siyuan","slug":"errinvalidboxid","errorCode":"ErrInvalidBoxID","errorMessage":"invalid box id","messagePattern":"invalid box id","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/av/av.go","lineNumber":1341,"sourceCode":"\tav := filepath.Join(util.DataDir, \"storage\", \"av\")\n\tret = filepath.Join(av, avID+\".json\")\n\tif !gulu.File.IsDir(av) {\n\t\tif err := os.MkdirAll(av, 0755); err != nil {\n\t\t\tlogging.LogErrorf(\"create attribute view dir failed: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc GetAttributeViewI18n(key string) string {\n\treturn util.AttrViewLangs[util.Lang][key].(string)\n}\n\nvar (\n\tErrAttributeViewNotFound  = errors.New(\"attribute view not found\")\n\tErrInvalidAttributeViewID = errors.New(\"invalid attribute view id\")\n\tErrInvalidBoxID           = errors.New(\"invalid box id\")\n\tErrViewNotFound           = errors.New(\"view not found\")\n\tErrKeyNotFound            = errors.New(\"key not found\")\n\tErrItemNotFound           = errors.New(\"item not found\")\n\tErrWrongLayoutType        = errors.New(\"wrong layout type\")\n\tErrInvalidColumnAlign     = errors.New(\"invalid column align\")\n\tErrSpecTooNew             = errors.New(\"attribute view spec is too new\")\n\tErrRichTextSpecMismatch   = errors.New(\"attribute view rich text requires storage spec 9\")\n\tErrFilterTooDeep          = errors.New(\"filter nesting depth exceeds the maximum allowed\")\n)\n\nconst (\n\tNodeAttrNameAvs        = \"custom-avs\"                  // 用于标记块所属的属性视图，逗号分隔 av id\n\tNodeAttrView           = \"custom-sy-av-view\"           // 用于标记块所属的属性视图视图 view id Database block support specified view https://github.com/siyuan-note/siyuan/issues/10443\n\tNodeAttrVisibleViewIDs = \"custom-sy-av-visible-views\"  // 用于标记数据库块显示的视图 ID，逗号分隔\n\tNodeAttrContextFilter  = \"custom-sy-av-context-filter\" // 用于保存数据库块独有的上下文筛选配置\n\tNodeAttrViewStaticText = \"custom-sy-av-s-text\"         // 用于标记块所属的属性视图静态文本 Database-bound block primary key supports setting static anchor text https://github.com/siyuan-note/siyuan/issues/10049\n\n\tNodeAttrViewNames = \"av-names\" // 用于临时标记块所属的属性视图名称，空格分隔","sourceCodeStart":1323,"sourceCodeEnd":1359,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/av/av.go#L1323-L1359","documentation":"ErrInvalidBoxID indicates the supplied notebook (box) ID is non-empty but fails ast.IsNodeIDPattern. Both writeAttributeViewData and the encrypted-data hook (encrypted_hook.go) validate the boxID before reading/writing AV data, because the box ID is used as key material in the encrypted notebook envelope and as a storage path segment.","triggerScenarios":"Passing a malformed boxID (garbage string, path fragment, extension included) to writeAttributeViewData or to AV operations that route through the encrypted hook's validation where boxID != \"\" and !ast.IsNodeIDPattern(boxID).","commonSituations":"Hardcoding a notebook name instead of its ID; deriving boxID from a filesystem path incorrectly; using an empty-ish placeholder that is non-empty but invalid; tests feeding deliberately wrong IDs (TestRejectInvalidAttributeViewPathIDs).","solutions":["Validate with ast.IsNodeIDPattern before calling; otherwise pass an empty boxID when the notebook is unknown and let the code resolve it","Obtain boxID via notebook listing (conf notebook root path -> id) instead of manual strings","Trim the ID from full paths with filepath.Base and strip extensions","For encrypted notebooks, double-check the ID matches the actual notebook opened in the workspace"],"exampleFix":"// before\nerr := av.WriteAttributeViewData(data, avID, boxID) // boxID = \"/data/notebooks/foo\" -> ErrInvalidBoxID\n// after\nboxID = filepath.Base(strings.TrimRight(boxID, \"/\"))\nif boxID != \"\" && !ast.IsNodeIDPattern(boxID) {\n    return fmt.Errorf(\"invalid box id: %q\", boxID)\n}\nerr := av.WriteAttributeViewData(data, avID, boxID)","handlingStrategy":"validation","validationCode":"if boxID != \"\" && !ast.IsNodeIDPattern(boxID) {\n    return fmt.Errorf(\"invalid box id: %q\", boxID)\n}","typeGuard":"func isValidBoxID(s string) bool { return s == \"\" || ast.IsNodeIDPattern(s) }","tryCatchPattern":"err := av.WriteAttributeViewData(data, avID, boxID)\nif errors.Is(err, av.ErrInvalidBoxID) {\n    return fmt.Errorf(\"boxID %q is not a notebook id; resolve it from conf notebooks first\", boxID)\n}","preventionTips":["Resolve boxID via the notebook listing API, never from a filesystem path segment guess","Pass \"\" when the notebook is unknown so the kernel resolves it, rather than a placeholder string","Validate with ast.IsNodeIDPattern before writes, especially for encrypted notebooks"],"tags":["go","attribute-view","id-validation","encrypted-notebook"],"backgroundTag":"invalid-identifier-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}