siyuan-note/siyuan · error

Field [batch] must not be empty

Error message

Field [batch] must not be empty

What it means

The /api/clipboard/cleanupRichText decoder requires a batch identifier string that is non-empty after trimming. The batch ID ties this cleanup call to a prior prepareRichText session; an empty or whitespace-only batch is rejected because no cleanup target can be resolved.

Solutions

  1. Pass the exact batch identifier string used/returned by the preceding prepareRichText call
  2. Trim and check the batch value client-side before calling the API
  3. Verify the prepare step actually succeeded and its batch ID was persisted before scheduling cleanup

Example fix

// before
fetchPost("/api/clipboard/cleanupRichText", {batch: batchId || "", groups})
// after
if (!batchId?.trim()) { return; }
fetchPost("/api/clipboard/cleanupRichText", {batch: batchId, groups})
Defensive patterns

Strategy: validation

Validate before calling

if (typeof batch !== 'string' || !batch.trim()) throw new Error('batch must be a non-empty string');

Prevention

When it happens

Trigger: POSTing {"batch":"", "groups":[...]} or {"batch":" ", ...} to /api/clipboard/cleanupRichText, or building the payload before the batch ID from prepareRichText was captured.

Common situations: Calling cleanup without first storing the batch returned/used by prepare; a variable holding the batch ID is empty due to a failed prepare call that was ignored; passing an ID field with the wrong key name so the default empty string is sent.

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


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

Appendix: source

Thrown at kernel/apicontract/clipboard_input.go:55

					return request, fmt.Errorf("Field [assets.%d.box] should be of type [String]", i)
				}
			}
			request.Assets = append(request.Assets, RichClipboardAsset{Index: int(index), Path: strings.TrimSpace(path), Box: strings.TrimSpace(box)})
		}
		return
	}
	CleanupRichText.decodeRequest = func(reader io.Reader) (request CleanupRichTextRequest, err error) {
		fields, err := blockRequestFields(reader, "/api/clipboard/cleanupRichText")
		if err != nil {
			return request, err
		}
		request.Batch, err = legacyField[string](fields, "batch", "String", true)
		if err != nil {
			return request, err
		}
		request.Batch = strings.TrimSpace(request.Batch)
		if request.Batch == "" {
			return request, fmt.Errorf("Field [batch] must not be empty")
		}
		groups, err := legacyField[[]json.RawMessage](fields, "groups", "Array", true)
		if err != nil {
			return request, err
		}
		request.Groups = make([]string, 0, len(groups))
		if len(groups) == 0 {
			return request, fmt.Errorf("Field [groups] must not be empty")
		}
		for i, raw := range groups {
			var group string
			if bytes.Equal(raw, []byte("null")) || json.Unmarshal(raw, &group) != nil {
				return request, fmt.Errorf("Field [groups.%d] should be of type [String]", i)
			}
			request.Groups = append(request.Groups, group)
		}
		return
	}

View on GitHub (pinned to 9f775e8a12)