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
- Re-fetch UnusedAttributeViews(false) and pick a currently listed unused ID before removing
- Refresh the database panel/UI so the stale entry disappears
- 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
- Re-fetch the unused list immediately before each removal
- Detach the database from its document before cleanup
- Avoid caching unused-AV IDs across sessions
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
- at least one visible view is required
- attribute view custom color index
- attribute view custom color must not be null
- attribute view custom colors count exceeds the
- attribute view custom colors must not be empty
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)
returnView on GitHub (pinned to 9f775e8a12)