siyuan-note/siyuan · error

Field [ ] must not be null

Error message

Field [%s] must not be null

What it means

In avDecodeBoundFields, a bound field present in the request but explicitly JSON null is rejected with 'Field [<name>] must not be null'. Bound fields carry structural payloads (e.g. filters data, attr values) that the decoder must normalize, and null would break the placeholder-based decoding.

Solutions

  1. Send an empty container instead of null: [] for array fields (data/values/blocksValues), {} for object fields (value/group)
  2. Coerce null to the empty default in the client before the request
  3. Omit nothing else — the field must exist with a real value

Example fix

// before
fetchPost("/api/av/setAttrViewSorts", {avID, data: null});
// after
fetchPost("/api/av/setAttrViewSorts", {avID, data: sorts ?? []});
Defensive patterns

Strategy: validation

Validate before calling

function deNull(obj) { for (const k of ["data","value","values","blocksValues","group"]) if (obj[k] === null) obj[k] = Array.isArray(obj[k]) || ["data","values","blocksValues"].includes(k) ? [] : {}; return obj; }

Type guard

const isNotNull = (v) => v !== null && v !== undefined;

Try / catch

try { await post(avEndpoint, payload); } catch (e) { if (/must not be null/.test(e.message)) { payload = withDefaults(payload); /* retry once */ } else throw e; }

Prevention

When it happens

Trigger: Sending JSON null for a bound field: {"avID":"...","data":null} to /api/av/setAttrViewFilters or setAttrViewSorts, {"value":null} to setAttributeViewBlockAttr, {"values":null} to batchSetAttributeViewBlockAttrs, {"blocksValues":null}, {"group":null}.

Common situations: Clients initializing fields to null as a default, JSON serializers emitting null for undefined values, migrations from older API versions where null was tolerated.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/av_decode.go:25

	"io"
	"reflect"
	"strings"
)

// avDecodeBoundFields 仅对原本经过 JSON 结构体绑定的字段保留大小写匹配和数字归一化。
func avDecodeBoundFields[Request any](reader io.Reader, path string, boundFields map[string]string, ignoredBools []string) (request Request, err error) {
	fields, err := blockRequestFields(reader, path)
	if err != nil {
		return request, err
	}
	original := map[string]json.RawMessage{}
	for name, placeholder := range boundFields {
		raw, present := fields[name]
		if !present {
			return request, fmt.Errorf("Field [%s] is required", name)
		}
		if bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
			return request, fmt.Errorf("Field [%s] must not be null", name)
		}
		original[name] = raw
		fields[name] = json.RawMessage(placeholder)
	}
	for _, name := range ignoredBools {
		original[name] = fields[name]
		delete(fields, name)
	}
	value := reflect.ValueOf(&request).Elem()
	if err = decodeRequestFields(value, fields); err != nil {
		return request, err
	}
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		name := field.Tag.Get("json")
		raw, present := original[name]
		if !present {
			continue

View on GitHub (pinned to 9f775e8a12)