siyuan-note/siyuan · error

cannot parse attribute view

Error message

cannot parse attribute view %s: %v

What it means

After JSON validation, scanViews parses the attribute-view definition with av.ParseAttributeViewByPath and requires the parsed view to be non-nil with an ID matching the filename. A parse failure, nil result, or ID mismatch means the definition's internal ID does not correspond to its storage filename, so the relinker stops to avoid relinking under the wrong identity.

Solutions

  1. Restore the original definition so the internal id matches the filename
  2. Rename the file back to <internal-id>.json so it matches the id field
  3. Use the app's duplicate-database feature instead of copying .json files manually
  4. Rebuild the AV from the notebook block if the definition is unrecoverable

Example fix

// before
// file: storage/av/20240101120000-abc123.json, content id: "20240101120000-xyz789"
// after
// rename file to match internal id
os.Rename("data/storage/av/20240101120000-abc123.json", "data/storage/av/20240101120000-xyz789.json")
Defensive patterns

Strategy: validation

Validate before calling

// confirm the internal id matches the filename before relinking
var probe struct { ID string `json:"id"` }
json.Unmarshal(data, &probe)
if probe.ID != idFromFilename {
	// rename the file to match the internal id, or restore the original
}

Prevention

When it happens

Trigger: storage/av/<id>.json is valid JSON but has an invalid schema, or the "id" field inside differs from <id> (file renamed/copied under a different ID, e.g. duplicating an AV file manually).

Common situations: A user copied an AV .json file to a new ID to 'clone' a database without updating its internal id; schema drift after downgrading/upgrading versions; hand-edited definitions.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/asset_relink.go:324

		}
		data, err := filelock.ReadFile(abs)
		if err != nil {
			return err
		}
		if util.IsCiphertext(data) {
			return fmt.Errorf("encrypted attribute view in global storage: %s", id)
		}
		if !json.Valid(data) {
			return fmt.Errorf("invalid attribute view JSON: %s", id)
		}
		if !p.mayContainReferences(data) {
			continue
		}
		p.parsedViews++
		cache.RemoveAVData(id)
		view, err := av.ParseAttributeViewByPath(abs)
		if err != nil || view == nil || view.ID != id {
			return fmt.Errorf("cannot parse attribute view %s: %v", id, err)
		}
		before := len(p.result.References)
		if err = p.attributeView(view, apicontract.AssetReference{AvID: id, Path: "storage/av/" + entry.Name()}); err != nil {
			return err
		}
		if len(p.result.References) > before {
			p.files = append(p.files, &assetRelinkFile{path: abs, before: data, view: view, items: p.referenceItems(before)})
			owners := []apicontract.AssetReference{}
			for _, tree := range p.trees {
				ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
					if entering && n.Type == ast.NodeAttributeView && n.AttributeViewID == id {
						owners = append(owners, apicontract.AssetReference{Notebook: tree.Box, RootID: tree.Root.ID, BlockID: n.ID})
					}
					return ast.WalkContinue
				})
			}
			if len(owners) > 0 {
				references := append([]apicontract.AssetReference(nil), p.result.References[before:]...)

View on GitHub (pinned to 9f775e8a12)