siyuan-note/siyuan · error

Field [ ] must not be null

Error message

Field [%s] must not be null

What it means

When a JSON field is explicitly null and the contract neither marks the field 'nullable' nor 'filterstrings' nor uses a pointer type, decodeRequestFields rejects it. JSON null is treated as a present-but-invalid value distinct from a missing key.

Solutions

  1. Omit the field entirely if it is optional, or send a concrete non-null value
  2. Change the contract field to a pointer type or add `nullable` to its api tag if null is legitimately allowed
  3. Fix client serialization to drop null fields (e.g. omitEmpty / conditional spread)

Example fix

// before
fetchPost(url, {id: blockId ?? null})
// after
const body: any = {}
if (blockId != null) { body.id = blockId }
fetchPost(url, body)
Defensive patterns

Strategy: validation

Validate before calling

function dropNulls(obj) {
  return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined))
}
// fetchPost(path, dropNulls(payload))

Type guard

function isNonNull<T>(v: T | null | undefined): v is T { return v !== null && v !== undefined }

Try / catch

try { await fetchPost(path, payload) } catch (e) { const m = e.message.match(/Field \[(.+?)\] must not be null/); if (m) { console.error(`Field "${m[1]}" cannot be null — omit it or supply a value`); return } throw e }

Prevention

When it happens

Trigger: Client sends {"id": null} for a field whose type is string/int/struct and whose api tag lacks 'nullable'; serializers emitting null for undefined values; Go/JS objects marshaled with nil fields.

Common situations: JavaScript `JSON.stringify({id: someUndefined})` when someUndefined is a null- valued variable; TypeScript interfaces with optional properties typed as T|null; database rows with NULL values passed straight through to the request.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/decode.go:77

				return fmt.Errorf("unsupported embedded request field: %s", field.Name)
			}
			if err := decodeRequestFields(value.Field(i), fields); err != nil {
				return err
			}
			continue
		}
		options := "," + field.Tag.Get("api") + ","
		has := func(option string) bool { return strings.Contains(options, ","+option+",") }
		raw, present := fields[name]
		if !present {
			if has("optional") {
				continue
			}
			return fmt.Errorf("Field [%s] is required", name)
		}
		isNull := bytes.Equal(bytes.TrimSpace(raw), []byte("null"))
		if isNull && !has("nullable") && field.Type.Kind() != reflect.Pointer && !has("filterstrings") {
			return fmt.Errorf("Field [%s] must not be null", name)
		}
		if has("filterstrings") {
			var entries []json.RawMessage
			if json.Unmarshal(raw, &entries) == nil {
				var ids []string
				for _, entry := range entries {
					var id string
					if !bytes.Equal(bytes.TrimSpace(entry), []byte("null")) && json.Unmarshal(entry, &id) == nil {
						ids = append(ids, id)
					}
				}
				value.Field(i).Set(reflect.ValueOf(ids))
			}
			continue
		}
		var decodeErr error
		if isNull && has("nullable") {
			value.Field(i).SetZero()

View on GitHub (pinned to 9f775e8a12)