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
- Obtain the deck ID from getRiffDeck IDs (doc/block node IDs) rather than constructing it manually
- Validate the ID shape (20-char node-ID pattern) before calling
- 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
- Always take deck IDs from API responses (getRiffDeckIDs), never fabricate them
- Trim and unescape IDs copied from URLs or logs
- Centralize a node-ID validator in client code
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
- Encrypted notebooks do not support this operation
- invalid frontend capability ID: %s
- invalid AI editor action ID
- invalid AI editor action data
- duplicate AI editor action ID [%s]
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/938b177cf623ddf1.
Report an issue: GitHub.