siyuan-note/siyuan · error

Encrypted notebooks do not support this operation

Error message

Encrypted notebooks do not support this operation

What it means

Thrown by ValidateFlashcardBlockIDs when at least one block ID fails the isSupportedFlashcardBlock check. That function returns false if treenode.GetBlockTreeInExactBox(blockID, "") returns nil (the block does not exist) OR if the block lives in an encrypted notebook (IsEncryptedBox(bt.BoxID) is true). The error message uses Conf.Language(313) = "Encrypted notebooks do not support this operation". This is a gatekeeper function that prevents encrypted content from entering the global flashcard deck store.

Source

Thrown at kernel/model/flashcard.go:47

	"github.com/88250/gulu"
	"github.com/88250/lute/ast"
	"github.com/88250/lute/parse"
	"github.com/open-spaced-repetition/go-fsrs/v3"
	"github.com/siyuan-note/filelock"
	"github.com/siyuan-note/logging"
	"github.com/siyuan-note/riff"
	"github.com/siyuan-note/siyuan/kernel/cache"
	"github.com/siyuan-note/siyuan/kernel/sql"
	"github.com/siyuan-note/siyuan/kernel/treenode"
	"github.com/siyuan-note/siyuan/kernel/util"
)

// ValidateFlashcardBlockIDs 只允许普通笔记本中可确认存在的块进入全局闪卡存储。
func ValidateFlashcardBlockIDs(blockIDs []string) error {
	for _, blockID := range blockIDs {
		if !isSupportedFlashcardBlock(blockID) {
			return errors.New(Conf.Language(313))
		}
	}
	return nil
}

func validateFlashcardTree(rootID string) error {
	return ValidateFlashcardBlockIDs([]string{rootID})
}

func isSupportedFlashcardBlock(blockID string) bool {
	bt := treenode.GetBlockTreeInExactBox(blockID, "")
	return bt != nil && !IsEncryptedBox(bt.BoxID)
}

func filterSupportedFlashcards(cards []riff.Card) (ret []riff.Card) {
	for _, card := range cards {
		if card == nil || !isSupportedFlashcardBlock(card.BlockID()) {
			continue

View on GitHub (pinned to 251596fc0d)

Solutions

  1. If the block is in an encrypted notebook, move it to a non-encrypted notebook first, or decrypt the notebook — encrypted content is fundamentally excluded from flashcard decks by design.
  2. If the block should exist, verify it is still present via the block lookup endpoint — it may have been deleted.
  3. As an API client, filter block IDs through isSupportedFlashcardBlock equivalent logic (exists + non-encrypted) before calling flashcard APIs.

Example fix

// before
await post('/api/riff/addRiffCards', { deckID, blockIDs: [...allSelectedBlocks] })

// after — exclude blocks from encrypted notebooks and non-existent blocks
const validBlockIDs = allSelectedBlocks.filter(id => {
  const bt = getBlockInfo(id)
  return bt && !bt.encrypted
})
await post('/api/riff/addRiffCards', { deckID, blockIDs: validBlockIDs })
Defensive patterns

Strategy: validation

Validate before calling

// Filter out blocks from encrypted notebooks before flashcard operations
function isFlashcardSupported(bt) {
  return bt != null && !bt.encrypted
}
const supportedBlockIDs = blockIDs.filter(id => isFlashcardSupported(blockTreeCache[id]))

Type guard

// Type guard: check if a block can be used as a flashcard
function isFlashcardEligible(bt: { boxID: string; encrypted: boolean } | null): bt is { boxID: string; encrypted: false } {
  return bt != null && !bt.encrypted
}

Prevention

When it happens

Trigger: Calling any flashcard API that routes through ValidateFlashcardBlockIDs (such as adding blocks to a deck or resetting flashcards) with a block ID that either does not exist in the block tree or resides in an encrypted notebook. The isSupportedFlashcardBlock check uses GetBlockTreeInExactBox with an empty box filter, so it looks up the block across all boxes.

Common situations: User selects blocks from an encrypted notebook and tries to add them as flashcards; a block was deleted after the UI rendered it but before the flashcard API call; an API client passes a stale or fabricated block ID.

Related errors


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