siyuan-note/siyuan · warning
at least one visible view is required
Error message
at least one visible view is required
What it means
Returned by SetDatabaseBlockVisibleViews (attribute_view.go:1240), dispatched through the transaction action "setAttrViewBlockVisibleViews". It is the first guard in the function: if len(operation.ViewIDs) < 1 the call is rejected because at least one view must remain visible. The same literal is reused at line 1264 (error 430) for a later, defensive re-check.
Source
Thrown at kernel/model/attribute_view.go:1240
func (tx *Transaction) doSetAttrViewBlockView(operation *Operation) (ret *TxErr) {
err := SetDatabaseBlockView(operation.BlockID, operation.AvID, operation.ID)
if err != nil {
return &TxErr{code: TxErrHandleAttributeView, id: operation.AvID, msg: err.Error()}
}
return
}
func (tx *Transaction) doSetAttrViewBlockVisibleViews(operation *Operation) (ret *TxErr) {
err := SetDatabaseBlockVisibleViews(operation.BlockID, operation.AvID, operation.ViewIDs)
if err != nil {
return &TxErr{code: TxErrHandleAttributeView, id: operation.AvID, msg: err.Error()}
}
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 {View on GitHub (pinned to 251596fc0d)
Solutions
- Ensure at least one view ID remains in the viewIDs payload before sending the transaction.
- On the UI side, prevent the user from deselecting the last visible view (disable the last checkbox while checked).
- If you intend to change selection, send the full new non-empty list atomically rather than clearing then setting.
Example fix
// before: can send an empty selection
transaction({ op: "setAttrViewBlockVisibleViews", viewIDs: selected })
// after: guard against empty
if (selected.length === 0) {
showMessage("At least one visible view is required")
return
}
transaction({ op: "setAttrViewBlockVisibleViews", viewIDs: selected }) Defensive patterns
Strategy: validation
Validate before calling
// Never submit an empty viewIDs list.
if (!viewIDs || viewIDs.length === 0) {
showMessage('At least one visible view is required')
return
} Type guard
function hasVisibleViewSelection(v: unknown): v is string[] {
return Array.isArray(v) && v.every(x => typeof x === 'string') && v.length >= 1
} Prevention
- Disable the control that deselects the last visible view.
- Send the full new selection atomically rather than clear-then-set.
- Treat an empty selection as a no-op, not a submission.
When it happens
Trigger: Frontend sends setAttrViewBlockVisibleViews with an empty viewIDs array (or null/undefined decoded to an empty slice), attempting to hide every view of a database block.
Common situations: UI bug unchecking the last visible view; plugin clears the list then re-adds in a second operation but sends the clear first; desync where the checkbox list is emptied before the new selection arrives.
Related errors
- view [%s] not found in attribute view [%s]
- 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/dd34bac9a59850d4.
Report an issue: GitHub.