siyuan-note/siyuan · error

encrypted attribute view snapshot is missing notebook contex

Error message

encrypted attribute view snapshot is missing notebook context

What it means

Thrown by decryptHistoricalAttributeView when the boxID derived from the file location is empty (global storage path) but the file content is detected as ciphertext by util.IsCiphertext. Decryption needs a notebook's data-encryption key, which can only be looked up by boxID, so an encrypted blob at a global path with no notebook context cannot be decrypted.

Source

Thrown at kernel/model/attribute_view_render.go:1262

	attrView = av.NewAttributeView(avID)
	if err = gulu.JSON.UnmarshalJSON(data, attrView); err != nil {
		logging.LogErrorf("unmarshal attribute view [%s] failed: %s", avID, err)
		return
	}
	if err = av.CheckSpec(attrView); nil != err {
		return
	}

	viewable, err = renderAttributeView(attrView, "", viewID, carrierViewID, query, page, pageSize, groupPaging, false, false, nil, "")
	return
}

func decryptHistoricalAttributeView(boxID, avID string, data []byte) ([]byte, error) {
	ciphertext := util.IsCiphertext(data)
	if boxID == "" {
		if ciphertext {
			return nil, errors.New("encrypted attribute view snapshot is missing notebook context")
		}
		return data, nil
	}
	if !IsEncryptedBox(boxID) {
		if ciphertext {
			return nil, fmt.Errorf("encrypted attribute view snapshot has no matching notebook [%s]", boxID)
		}
		return data, nil
	}
	if !ciphertext {
		return nil, fmt.Errorf("encrypted notebook attribute view snapshot is plaintext [%s]", boxID)
	}
	return av.DecryptAVData(boxID, avID, data)
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Restore the file to its correct notebook-scoped path (/<boxID>/storage/av/<avID>.json) so the boxID can be inferred and the matching key used.
  2. If the owning notebook is unknown/removed, the data cannot be recovered through this path; restore from a backup that preserves the notebook structure.
  3. Prevent the migration by ensuring sync/import keeps encrypted-notebook objects under their notebook path.
Defensive patterns

Strategy: try-catch

Try / catch

data, err := decryptHistoricalAttributeView(boxID, avID, data)
if err != nil {
    if err.Error() == "encrypted attribute view snapshot is missing notebook context" {
        // AV is encrypted but its notebook context was lost; restore correct path or skip
    }
    return err
}

Prevention

When it happens

Trigger: An encrypted notebook's attribute view file has migrated to the global /storage/av/ location (no leading <boxID> segment) via sync, import, or a layout change. The decryptor sees ciphertext but has no boxID to select the key. Can be reached from RenderRepoSnapshotAttributeView (avBoxIDFromRepoPath returns "") or RenderHistoryAttributeView (source.boxID empty).

Common situations: Encrypted-notebook AV files ending up at global paths after data repo restore, cross-workspace sync where the notebook envelope was not preserved, or history generated before the boxID-path convention was enforced. The data is intact but its owner context was lost.

Related errors


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