siyuan-note/siyuan · error

Conf.Language(396)

Error message

Conf.Language(396)

What it means

When pinning, UpdatePinnedDocs resolves each ID via treenode.GetBlockTree and refuses to pin documents that live in an encrypted notebook. Instead of a hard-coded message it returns the localized user-facing string Conf.Language(396), which explains that encrypted notebooks do not support this operation. This keeps encrypted-notebook restrictions consistent with other kernel operations.

Solutions

  1. Move the document to a non-encrypted notebook before pinning it.
  2. Skip pinning documents whose notebook is encrypted; filter by IsEncryptedBox before calling.
  3. Handle the localized message in the client UI as an informational restriction rather than retrying.

Example fix

// before
model.UpdatePinnedDocs([]string{docID}, "pin", "", false)
// after
if bt := treenode.GetBlockTree(docID); bt == nil || model.IsEncryptedBox(bt.BoxID) {
    return // do not attempt to pin encrypted-notebook docs
}
model.UpdatePinnedDocs([]string{docID}, "pin", "", false)
Defensive patterns

Strategy: validation

Validate before calling

bt := treenode.GetBlockTree(docID)
if bt != nil && model.IsEncryptedBox(bt.BoxID) {
    return errors.New("refusing to pin document in encrypted notebook")
}

Type guard

func isPlainBox(boxID string) bool { return !model.IsEncryptedBox(boxID) }

Try / catch

if err := model.UpdatePinnedDocs(ids, "pin", "", false); err != nil {
    if err.Error() == model.Conf.Language(396) {
        // surface encrypted-notebook restriction to the user
    }
}

Prevention

When it happens

Trigger: Calling UpdatePinnedDocs with action="pin" and an ids entry whose block-tree record points into a notebook where IsEncryptedBox(bt.BoxID) is true (an encrypted notebook). Unpinning skips this check entirely.

Common situations: User or plugin tries to pin a document inside an encrypted notebook via the pin UI, API, or a script; encrypted-notebook content is being manipulated by an external tool that ignores the restriction.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/pinned_docs.go:186

	if err != nil {
		return err
	}
	selected := map[string]bool{}
	refs := []pinnedDocRef{}
	for _, id := range ids {
		if !ast.IsNodeIDPattern(id) {
			return fmt.Errorf("invalid document ID [%s]", id)
		}
		if selected[id] {
			continue
		}
		selected[id] = true
		if action == "unpin" {
			continue
		}
		bt := treenode.GetBlockTree(id)
		if bt != nil && IsEncryptedBox(bt.BoxID) {
			return fmt.Errorf("%s", Conf.Language(396))
		}
		if !isPinnableDocument(bt) {
			return fmt.Errorf("document [%s] cannot be pinned", id)
		}
		box := Conf.Box(bt.BoxID)
		if box == nil || box.Stat(bt.Path) == nil {
			return fmt.Errorf("document [%s] is unavailable", id)
		}
		ial := box.docIAL(bt.Path)
		if ial == nil {
			return fmt.Errorf("cannot read pinned document [%s]", id)
		}
		if id != bt.BoxID && ial[DocHiddenAttr] == "true" {
			return fmt.Errorf("document [%s] cannot be pinned", id)
		}
		refs = append(refs, pinnedDocRef{ID: id, Notebook: bt.BoxID})
	}
	if action == "pin" && selected[targetID] {

View on GitHub (pinned to 9f775e8a12)