siyuan-note/siyuan · error

Field [srcs] must not be empty

Error message

Field [srcs] must not be empty

What it means

decodeCopyFiles in kernel/apicontract/file_input.go requires the "srcs" array field to be present and contain at least one entry; an empty array fails with "Field [srcs] must not be empty". Copying files with no sources is a no-op the API refuses rather than silently succeeding.

Solutions

  1. Ensure srcs contains at least one path before calling the API
  2. Skip the API call entirely when the source list is empty client-side
  3. Check the selection/filter logic that builds srcs
  4. Include destDir alongside a non-empty srcs list as the endpoint expects both

Example fix

// before
fetchPost("/api/file/copyFiles", { srcs: [], destDir })
// after
if (!srcs.length) return; fetchPost("/api/file/copyFiles", { srcs, destDir })
Defensive patterns

Strategy: validation

Validate before calling

function requireNonEmptySrcs(srcs) {
  if (!Array.isArray(srcs) || srcs.length === 0) throw new Error("srcs must contain at least one path");
}
requireNonEmptySrcs(srcs);

Type guard

const hasSrcs = (p) => Array.isArray(p.srcs) && p.srcs.length > 0;

Try / catch

try { await copyFiles({ srcs, destDir }); } catch (e) { if (String(e).includes("srcs] must not be empty")) return; /* skip empty batch */ throw e; }

Prevention

When it happens

Trigger: Calling the copy-files API (legacyField[[]json.RawMessage] with required Array) with srcs: [] or srcs omitted-and-defaulted to empty after successful decoding.

Common situations: File manager integrations computing the source list from a selection that ended up empty; filtering out all candidate files before the call; loops that batch-copy and hit the final empty batch.

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

Appendix: source

Thrown at kernel/apicontract/file_input.go:21

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

func decodeCopyFiles(reader io.Reader, path string, trim bool) (request CopyFilesRequest, err error) {
	fields, err := blockRequestFields(reader, path)
	if err != nil {
		return request, err
	}
	srcs, err := legacyField[[]json.RawMessage](fields, "srcs", "Array", true)
	if err != nil {
		return request, err
	}
	if len(srcs) == 0 {
		return request, fmt.Errorf("Field [srcs] must not be empty")
	}
	request.DestDir, err = legacyField[string](fields, "destDir", "String", true)
	if err != nil {
		return request, err
	}
	for _, raw := range srcs {
		var src string
		if bytes.Equal(raw, []byte("null")) || json.Unmarshal(raw, &src) != nil {
			return request, fmt.Errorf("Field [srcs]: each element should be of type [String]")
		}
		if trim {
			src = strings.TrimSpace(src)
			if src == "" {
				return request, fmt.Errorf("Field [srcs]: path must not be empty")
			}
		}
		request.Srcs = append(request.Srcs, src)
	}

View on GitHub (pinned to 9f775e8a12)