siyuan-note/siyuan · error

Field [rootIDs] should be of type [Array]

Error message

Field [rootIDs] should be of type [Array]

What it means

The storage (SetWorkspaceData / rootIDs-style) decoder accepts an optional 'rootIDs' field but, if present, it must be a JSON array of strings. A non-array value (object, string, number) fails with 'Field [rootIDs] should be of type [Array]'. Non-string or empty entries are silently skipped rather than erroring.

Solutions

  1. Send rootIDs as a JSON array of strings, e.g. ["20240101120000-abcdefg"]
  2. Wrap a single ID: rootIDs: [id] instead of rootIDs: id
  3. Pass rootIDs:null or omit it to indicate 'all'
  4. Filter out null/undefined entries before serializing

Example fix

// before
body: JSON.stringify({ rootIDs: '20240101120000-abcdefg' })
// after
body: JSON.stringify({ rootIDs: ['20240101120000-abcdefg'] })
Defensive patterns

Strategy: type-guard

Validate before calling

function assertRootIDs(v: unknown): string[] {
  if (v === null || v === undefined) return [];
  if (!Array.isArray(v)) throw new Error('rootIDs must be an array of string IDs');
  return v.filter((id): id is string => typeof id === 'string' && id !== '');
}

Type guard

function isStringArray(v: unknown): v is string[] { return Array.isArray(v) && v.every((x) => typeof x === 'string'); }

Try / catch

try { await setStorage(data); } catch (e) { if (String(e).includes('rootIDs') && String(e).includes('Array')) { data.rootIDs = [].concat(data.rootIDs); retry(); } }

Prevention

When it happens

Trigger: Calling the storage endpoint with rootIDs set to a single string, an object, or a number instead of an array of document ID strings; or rootIDs:null (which is accepted, not an error).

Common situations: Clients passing a single ID instead of wrapping it in an array, JS objects used as ID sets serialized as JSON objects, or older API usage where rootIDs was a comma-separated string.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/storage.go:23

	"encoding/json"
	"errors"
	"io"
	"strings"
)

func init() {
	BatchUpdateRecentDocCloseTime.decodeRequest = func(reader io.Reader) (request RecentDocsUpdateRequest, err error) {
		fields, err := blockRequestFields(reader, "/api/storage/batchUpdateRecentDocCloseTime")
		if err != nil {
			return request, err
		}
		raw := fields["rootIDs"]
		if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
			return request, nil
		}
		var entries []json.RawMessage
		if json.Unmarshal(raw, &entries) != nil {
			return request, errors.New("Field [rootIDs] should be of type [Array]")
		}
		for _, entry := range entries {
			var id string
			if json.Unmarshal(entry, &id) == nil && id != "" {
				request.RootIDs = append(request.RootIDs, id)
			}
		}
		return
	}
	plain := PatchViewState
	PatchViewState.decodeRequest = func(reader io.Reader) (ViewStatePatchRequest, error) {
		request, err := plain.Decode(reader)
		if err != nil {
			switch {
			case strings.HasPrefix(err.Error(), "Field [values]"):
				err = errors.New("Field [values]: should be of type [Object]")
			case strings.HasPrefix(err.Error(), "Field [removeKeys]"):
				err = errors.New("Field [removeKeys]: each element should be a non-empty String")

View on GitHub (pinned to 9f775e8a12)