siyuan-note/siyuan · error

invalid attribute view ID

Error message

invalid attribute view ID: %s

What it means

ValidateUnusedAttributeView first checks that the supplied ID matches the node-ID pattern (ast.IsNodeIDPattern). A non-conforming ID fails immediately with this error. This is a defensive check so downstream file operations (which build paths from the ID) cannot be abused for path traversal or target arbitrary files.

Solutions

  1. Pass the exact attribute-view ID as listed by UnusedAttributeViews output (item.Item)
  2. Validate the ID client-side against the node-ID pattern before calling
  3. Do not accept free-text IDs from users in plugins/scripts; select from the unused-AV list instead

Example fix

// before
fetchPost('/api/attr/removeUnusedAttributeView', { id: userInput })
// after
if (!/^[0-9a-f]{20,}$/.test(id)) throw new Error('bad AV id');
fetchPost('/api/attr/removeUnusedAttributeView', { id })
Defensive patterns

Strategy: validation

Validate before calling

// SiYuan node IDs are time-ordered hex strings
function looksLikeNodeID(id) {
  return typeof id === 'string' && /^[0-9a-f]{14,26}$/.test(id);
}
if (!looksLikeNodeID(id)) throw new Error('invalid AV id');

Type guard

const isNodeID = (v) => typeof v === 'string' && v.length > 0 && !v.includes('/') && !v.includes('..') && /^[0-9a-f]+$/.test(v);

Try / catch

try { await removeUnusedAttributeView(id) } catch (e) {
  if (e.message.includes('invalid attribute view ID')) {
    // caller supplied a malformed id; surface to user/plugin developer
  }
}

Prevention

When it happens

Trigger: Calling the RemoveUnusedAttributeView API (or ValidateUnusedAttributeView directly) with an id that is not a 64-bit-hex-style node ID — e.g. containing '/', '..\\', empty string, or random text.

Common situations: Hand-crafted API calls with guessed or user-supplied IDs; plugins passing a database/AV key instead of a block/AV ID; URL-encoding or truncation mangling the ID; hostile requests probing for path traversal.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/attribute_view.go:57

	"github.com/88250/lute/ast"
	"github.com/88250/lute/parse"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/siyuan-note/filelock"
	"github.com/siyuan-note/logging"
	"github.com/siyuan-note/siyuan/kernel/av"
	"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) {

View on GitHub (pinned to 9f775e8a12)