siyuan-note/siyuan · error · ErrViewNotFound
view not found
Error message
view not found
What it means
ErrViewNotFound is a sentinel error in the SiYuan attribute-view (database) engine. It means no view with the given view ID exists inside the AttributeView, or the attribute-view JSON file itself could not be found on disk (parseAttributeViewByPathInBox at kernel/av/av.go:785 maps a missing file to this same error). Views are per-AttributeView objects stored in av.Views; GetFirstView (kernel/av/av.go:1061) also returns it when the Views array holds no usable view.
Solutions
- Fetch fresh view IDs first: parse the AttributeView (av.ParseAttributeView) and pick the ID from attrView.Views or GetVisibleViewIDs instead of reusing a cached viewID
- If the database block is stale, clear or update its custom-sy-av-view attribute so it falls back to the first view
- Check that the attribute-view file exists before parsing if the avID may have been deleted (filelock.IsExist on the resolved path)
- In API handlers, branch on errors.Is(err, av.ErrViewNotFound) and return the viewNotFound marker so the frontend can re-request views
Example fix
// before: viewID cached from an earlier response
view, av2, _, err := model.RenderAttributeViewWithTarget(blockID, avID, cachedViewID, ...)
if err != nil { return err }
// after: validate the view ID against the current attribute view
attrView, err := av.ParseAttributeView(avID)
if err != nil { return err }
if nil == attrView.GetView(cachedViewID) {
cachedViewID = "" // let the renderer pick the first visible view
}
view, av2, _, err := model.RenderAttributeViewWithTarget(blockID, avID, cachedViewID, ...)
if errors.Is(err, av.ErrViewNotFound) {
// re-read views and retry once with the first view
} Defensive patterns
Strategy: validation
Validate before calling
attrView, err := av.ParseAttributeView(avID)
if err != nil {
return err // includes ErrViewNotFound when the .av.json is missing
}
view := attrView.GetView(viewID)
if nil == view {
ids := attrView.GetVisibleViewIDs("")
if 0 == len(ids) {
return av.ErrViewNotFound // no views at all
}
viewID = ids[0]
} Type guard
func isViewNotFound(err error) bool {
return errors.Is(err, av.ErrViewNotFound)
} Try / catch
if err != nil {
if errors.Is(err, av.ErrViewNotFound) {
// refresh views, fall back to first visible view, or show 'view deleted' UI
}
} Prevention
- Never cache viewIDs across renders; take them from the latest getAttributeView response
- When deleting a view, update dependent database-block attributes (custom-sy-av-view) in the same transaction
- Treat an empty view list as data corruption: re-create a default table view instead of retrying
When it happens
Trigger: Calling model.RenderAttributeViewWithTarget or any transaction operation with a viewID that is not in attrView.Views; a database block whose custom-sy-av-view attribute points to a deleted view; GetFirstView on an AttributeView with an empty Views slice; ParseAttributeView on an avID whose storage/{avID}.json file no longer exists (parseAttributeViewByPathInBox sets err = ErrViewNotFound when filelock.IsExist fails). The API layer (kernel/api/av.go:1370) detects it and returns ret.Data = {"error": "viewNotFound"}.
Common situations: A view tab was deleted in another tab/window while a stale render request arrives; sync or data rollback removed the .av.json; frontend holds a cached viewID after the database was rebuilt; code copies a viewID from one AttributeView to another (IDs are only unique per AttributeView).
Related errors
- item not found
- key not found
- attribute view not found
- attribute view [ ] not found in box [ ]
- attribute view spec is too new
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/5da3439748b66085.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/av/av.go:1296
ret = filepath.Join(av, avID+".json")
if !gulu.File.IsDir(av) {
if err := os.MkdirAll(av, 0755); err != nil {
logging.LogErrorf("create attribute view dir failed: %s", err)
return
}
}
return
}
func GetAttributeViewI18n(key string) string {
return util.AttrViewLangs[util.Lang][key].(string)
}
var (
ErrAttributeViewNotFound = errors.New("attribute view not found")
ErrInvalidAttributeViewID = errors.New("invalid attribute view id")
ErrInvalidBoxID = errors.New("invalid box id")
ErrViewNotFound = errors.New("view not found")
ErrKeyNotFound = errors.New("key not found")
ErrItemNotFound = errors.New("item not found")
ErrWrongLayoutType = errors.New("wrong layout type")
ErrInvalidColumnAlign = errors.New("invalid column align")
ErrSpecTooNew = errors.New("attribute view spec is too new")
ErrFilterTooDeep = errors.New("filter nesting depth exceeds the maximum allowed")
)
const (
NodeAttrNameAvs = "custom-avs" // 用于标记块所属的属性视图,逗号分隔 av id
NodeAttrView = "custom-sy-av-view" // 用于标记块所属的属性视图视图 view id Database block support specified view https://github.com/siyuan-note/siyuan/issues/10443
NodeAttrVisibleViewIDs = "custom-sy-av-visible-views" // 用于标记数据库块显示的视图 ID,逗号分隔
NodeAttrViewStaticText = "custom-sy-av-s-text" // 用于标记块所属的属性视图静态文本 Database-bound block primary key supports setting static anchor text https://github.com/siyuan-note/siyuan/issues/10049
NodeAttrViewNames = "av-names" // 用于临时标记块所属的属性视图名称,空格分隔
)
View on GitHub (pinned to afa823b6b4)