siyuan-note/siyuan · error

invalid deck ID

Error message

invalid deck ID

What it means

RemoveDeck validates that deckID looks like a SiYuan node ID (ast.IsNodeIDPattern: 20-char base32-ish ID) before touching the deck store. Riff decks are keyed by document/block node IDs, so any other string cannot designate a deck and the call fails immediately with 'invalid deck ID'.

Source

Thrown at kernel/model/flashcard.go:1217

func RenameDeck(deckID, name string) (err error) {
	deckLock.Lock()
	defer deckLock.Unlock()

	waitForSyncingStorages()

	deck := Decks[deckID]
	deck.Name = name
	err = deck.Save()
	if err != nil {
		logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
		return
	}
	return
}

func RemoveDeck(deckID string) (err error) {
	if !ast.IsNodeIDPattern(deckID) {
		err = errors.New("invalid deck ID")
		return
	}

	deckLock.Lock()
	defer deckLock.Unlock()

	waitForSyncingStorages()

	riffSavePath := getRiffDir()
	deckPath := filepath.Join(riffSavePath, deckID+".deck")
	if filelock.IsExist(deckPath) {
		if err = filelock.Remove(deckPath); err != nil {
			return
		}
	}

	cardsPath := filepath.Join(riffSavePath, deckID+".cards")
	if filelock.IsExist(cardsPath) {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Obtain the deck ID from getRiffDeck IDs (doc/block node IDs) rather than constructing it manually
  2. Validate the ID shape (20-char node-ID pattern) before calling
  3. Check for copy/paste corruption such as surrounding quotes, whitespace, or URL escaping

Example fix

// before
await fetchPost('/api/riff/removeRiffDeck', {deck: 'my-deck-1'});
// after
const deckID = '20240101120000-abcdef1234567'; // real node ID from getRiffDeckIDs
if (!/^[0-9a-z]{20}$/.test(deckID)) throw new Error('invalid deck ID');
await fetchPost('/api/riff/removeRiffDeck', {deck: deckID});
Defensive patterns

Strategy: validation

Validate before calling

const isNodeID = (id) => typeof id === 'string' && /^[0-9a-v]{20}$/.test(id);
if (!isNodeID(deckID)) throw new Error('invalid deck ID: ' + deckID);

Type guard

const isNodeID = (id) => typeof id === 'string' && /^[0-9a-v]{20}$/.test(id);

Try / catch

try {
  await fetchPost('/api/riff/removeRiffDeck', {deck: deckID});
} catch (e) {
  if (String(e.msg).includes('invalid deck ID')) console.warn('bad deck ID', deckID);
  else throw e;
}

Prevention

When it happens

Trigger: Calling RemoveDeck (HTTP API /api/riff/removeRiffDeck) with a deckID that is not a valid node ID — empty string, a numeric ID, a UUID, a truncated or mistyped ID.

Common situations: Hard-coded or hand-written deck IDs in scripts; using a card/block ID of wrong length copied with extra characters; passing a custom deck name from another SRS tool.

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@8641553a1f (2026-09-11). Data as JSON: /api/errors/938b177cf623ddf1. Report an issue: GitHub.