siyuan-note/siyuan · error

attribute view is not unused

Error message

attribute view is not unused: %s

What it means

The supplied attribute-view ID is syntactically valid but is not present in the current list of unused attribute views (UnusedAttributeViews). The kernel refuses to remove an AV that is still referenced by documents, preventing deletion of databases in use.

Solutions

  1. Re-fetch UnusedAttributeViews(false) and pick a currently listed unused ID before removing
  2. Refresh the database panel/UI so the stale entry disappears
  3. Verify the AV is no longer referenced by any document (remove the database block first) before cleanup

Example fix

// before: using stale id
removeUnusedAttributeView(staleId)
// after: re-validate from fresh list
const unused = await getUnusedAttributeViews();
if (unused.some(i => i.Item === id)) removeUnusedAttributeView(id);
Defensive patterns

Strategy: validation

Validate before calling

const unused = await getUnusedAttributeViews();
if (!unused.some(i => i.Item === id)) throw new Error('AV is still in use or stale');

Try / catch

try { await removeUnusedAttributeView(id) } catch (e) {
  if (e.message.includes('not unused')) {
    await refreshUnusedList();  // id is stale or AV got re-attached
  }
}

Prevention

When it happens

Trigger: Calling RemoveUnusedAttributeView with an AV ID that is still bound to a document's attribute view, was already removed (stale ID from a cached list), or the unused list was refreshed since the ID was obtained.

Common situations: Stale UI/plugin cache showing an AV as unused after it was re-attached; racing with another client that removed or re-used the AV; typo'd ID coincidentally matching a valid-format ID of an in-use AV.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/eda3e84af58f2e0e. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/attribute_view.go:64

	"github.com/siyuan-note/siyuan/kernel/cache"
	"github.com/siyuan-note/siyuan/kernel/filesys"
	"github.com/siyuan-note/siyuan/kernel/search"
	"github.com/siyuan-note/siyuan/kernel/sql"
	"github.com/siyuan-note/siyuan/kernel/treenode"
	"github.com/siyuan-note/siyuan/kernel/util"
	"github.com/xrash/smetrics"
)

func ValidateUnusedAttributeView(id string) error {
	if !ast.IsNodeIDPattern(id) {
		return fmt.Errorf("invalid attribute view ID: %s", id)
	}
	for _, item := range UnusedAttributeViews(false) {
		if item.Item == id {
			return nil
		}
	}
	return fmt.Errorf("attribute view is not unused: %s", id)
}

func RemoveUnusedAttributeView(id string) (err error) {
	// 防御性校验:ID 必须是合法的节点 ID 格式,防止通过路径穿越读取或删除任意文件
	if err = ValidateUnusedAttributeView(id); err != nil {
		return
	}

	base := filepath.Join(util.DataDir, "storage", "av")
	absPath := filepath.Join(base, id+".json")
	if !filelock.IsExist(absPath) {
		return fmt.Errorf("attribute view not found: %s", id)
	}

	historyDir, err := getHistoryDir(HistoryOpClean)
	if err != nil {
		logging.LogErrorf("get history dir failed: %s", err)
		return

View on GitHub (pinned to 9f775e8a12)