siyuan-note/siyuan · error

Field [assets] must not be empty

Error message

Field [assets] must not be empty

What it means

The /api/clipboard/prepareRichText request decoder requires the assets array to contain at least one element. An empty assets array passes the type check (it is an Array) but is rejected because preparing rich text with no assets is meaningless.

Solutions

  1. Ensure at least one asset object (with index and path) is included in the assets array
  2. Check upstream selection logic so the API is only called when there is content to prepare
  3. Handle the error client-side by skipping the call when the asset list is empty

Example fix

// before
fetchPost("/api/clipboard/prepareRichText", {assets: []})
// after
if (assets.length > 0) { fetchPost("/api/clipboard/prepareRichText", {assets}) }
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(assets) || assets.length === 0) throw new Error('assets must be a non-empty array');

Prevention

When it happens

Trigger: POSTing {"assets": []} to /api/clipboard/prepareRichText, or omitting asset entries while building the payload programmatically.

Common situations: Frontend collects no selected blocks/assets before calling the API; a loop that conditionally appends assets never runs; copying an empty selection.

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/ad12d92cdf2c45b7. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/clipboard_input.go:22

	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"strings"
)

func init() {
	PrepareRichText.decodeRequest = func(reader io.Reader) (request PrepareRichTextRequest, err error) {
		fields, err := blockRequestFields(reader, "/api/clipboard/prepareRichText")
		if err != nil {
			return request, err
		}
		assets, err := legacyField[[]json.RawMessage](fields, "assets", "Array", true)
		if err != nil {
			return request, err
		}
		if len(assets) == 0 {
			return request, fmt.Errorf("Field [assets] must not be empty")
		}
		request.Assets = make([]RichClipboardAsset, 0, len(assets))
		for i, raw := range assets {
			var asset map[string]json.RawMessage
			if json.Unmarshal(raw, &asset) != nil || asset == nil {
				return request, fmt.Errorf("Field [assets.%d] should be of type [Object]", i)
			}
			var index float64
			var path, box string
			if bytes.Equal(asset["index"], []byte("null")) || json.Unmarshal(asset["index"], &index) != nil || index < 0 || index != float64(int(index)) || json.Unmarshal(asset["path"], &path) != nil || strings.TrimSpace(path) == "" {
				return request, fmt.Errorf("Invalid rich clipboard asset at index [%d]", i)
			}
			if value, exists := asset["box"]; exists {
				if bytes.Equal(value, []byte("null")) || json.Unmarshal(value, &box) != nil {
					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)})

View on GitHub (pinned to 9f775e8a12)