siyuan-note/siyuan · error
view [%s] not found in attribute view [%s]
Error message
view [%s] not found in attribute view [%s]
What it means
Returned by SetDatabaseBlockVisibleViews (attribute_view.go:1252) when one of the viewIDs passed to setAttrViewBlockVisibleViews does not resolve via attrView.GetView — i.e. the ID is not present in the attribute view's Views collection. The check runs before normalization, so a single bad ID aborts the whole call.
Source
Thrown at kernel/model/attribute_view.go:1252
}
return
}
func SetDatabaseBlockVisibleViews(blockID, avID string, viewIDs []string) (err error) {
if 1 > len(viewIDs) {
return errors.New("at least one visible view is required")
}
attrView, err := av.ParseAttributeView(avID)
if nil != err {
logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err)
return
}
visible := map[string]bool{}
for _, viewID := range viewIDs {
if nil == attrView.GetView(viewID) {
return fmt.Errorf("view [%s] not found in attribute view [%s]", viewID, avID)
}
visible[viewID] = true
}
var normalized []string
for _, view := range attrView.Views {
if visible[view.ID] {
normalized = append(normalized, view.ID)
}
}
if 1 > len(normalized) {
return errors.New("at least one visible view is required")
}
node, tree, err := getNodeByBlockID(nil, blockID)
if nil != err {
return
}View on GitHub (pinned to 251596fc0d)
Solutions
- Refresh the view list from av.GetAttributeView before submitting and drop any ID no longer present.
- Send only view IDs returned by a recent render of the same attribute view (operation.AvID must match).
- If mirroring, confirm each view ID belongs to operation.AvID, not a sibling database.
Example fix
// before: submitting a possibly stale id
transaction({ op: "setAttrViewBlockVisibleViews", avID, viewIDs: ["20240101000000-old"] })
// after: filter against the live view list
const live = await fetchAttrView(avID)
const viewIDs = selected.filter(id => live.views.some(v => v.id === id))
if (viewIDs.length) transaction({ op: "setAttrViewBlockVisibleViews", avID, viewIDs }) Defensive patterns
Strategy: validation
Validate before calling
// Filter submitted viewIDs against the live attribute view before sending.
const live = await fetchAttrView(avID)
const valid = viewIDs.filter(id => live.views.some(v => v.id === id))
if (valid.length !== viewIDs.length) throw new Error('one or more view IDs not found') Type guard
function viewExists(av: AttributeView, id: string): boolean {
return av.views.some(v => v.id === id)
} Prevention
- Re-fetch the attribute view before changing visible-view selection.
- Ensure each viewID belongs to the same avID being targeted.
- Drop deleted view IDs from the UI immediately after a view-delete transaction.
When it happens
Trigger: Frontend sends a view ID that was deleted, belongs to a different attribute view, or is a stale/corrupted value carried over from an older avID. Duplicates are tolerated (they just set the map key twice), but any unknown ID fails.
Common situations: View was deleted in another mirror of the same database but the local UI list still shows it; plugin hard-codes a view ID; avID/viewID mismatch after a database was duplicated and IDs were not remapped.
Related errors
- at least one visible view is required
- block [%s] is not an instance of attribute view [%s]
- invalid card aspect ratio preset [%v]
- invalid card aspect ratio [%v]
- invalid card size preset [%v]
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/1199b3e50a28852b.
Report an issue: GitHub.