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

  1. Refresh the view list from av.GetAttributeView before submitting and drop any ID no longer present.
  2. Send only view IDs returned by a recent render of the same attribute view (operation.AvID must match).
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/1199b3e50a28852b. Report an issue: GitHub.