siyuan-note/siyuan · warning
block updates are empty
Error message
block updates are empty
What it means
buildBlockUpdateOperations (the engine behind BuildBlockUpdateOperations) rejects the request up front when the inputs array is empty, returning "block updates are empty". Batch block updates require at least one BlockUpdateInput; there is nothing to validate or transact otherwise.
Solutions
- Check inputs.length (or operations.length) > 0 before invoking the update API and skip the call when empty.
- Ensure your local filter/validation isn't stripping all entries before the call.
- Guard the flush call site with a flag that tracks whether any update was actually queued.
Example fix
// before
await fetchPost('/api/block/updateBlock', { operations: pending });
// after
if (pending.length > 0) {
await fetchPost('/api/block/updateBlock', { operations: pending });
} Defensive patterns
Strategy: validation
Validate before calling
function assertNonEmpty(updates) {
if (!Array.isArray(updates) || updates.length === 0) {
throw new SkipUpdate('nothing to update');
}
} Prevention
- Guard batch flush calls with a length check.
- Track a dirty flag instead of flushing on a timer regardless of pending work.
- Ensure filters that trim invalid updates also cancel the flush when they empty the batch.
When it happens
Trigger: Calling the block update API with an empty operations/inputs list — e.g. a caller that filtered out all pending updates locally, or passed an uninitialized array.
Common situations: Plugins that accumulate edits into a batch and flush only when something changed, but flush unconditionally; clients serializing an empty selection or clipboard state.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Conf.Language(126)
- Field [ ] must not be empty
- Field [ ] must not be empty
- parse tree failed
- response.msg
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/c6d72ecc0d9d4003.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/block_update.go:85
for _, queued := range takeQueuedTransactions() {
flushTx(queued)
}
operations, rootIDs, err := build(inputs)
if err != nil {
return nil, nil, err
}
transaction := &Transaction{DoOperations: operations}
if err = performTxSyncLocked(transaction); err != nil {
return nil, nil, err
}
return []*Transaction{transaction}, rootIDs, nil
}
func buildBlockUpdateOperations(inputs []BlockUpdateInput, resolveTree blockUpdateTreeResolver, loadTree blockUpdateTreeLoader) (operations []*Operation, rootIDs []string, err error) {
if 1 > len(inputs) {
return nil, nil, errors.New("block updates are empty")
}
luteEngine := util.NewLute()
rootIDSet := map[string]struct{}{}
treeCache := map[blockUpdateTreeKey]*parse.Tree{}
for _, input := range inputs {
if !ast.IsNodeIDPattern(input.ID) {
return nil, nil, fmt.Errorf("invalid block ID [%s]", input.ID)
}
data, dataTree, parseErr := parseBlockUpdateData(input.Data, input.DataType, luteEngine)
if parseErr != nil {
return nil, nil, parseErr
}
var oldTree *parse.Tree
var cacheKey blockUpdateTreeKey
hasCacheKey := falseView on GitHub (pinned to 9f775e8a12)