{"record":{"id":"9912a8f0b0eac440","repo":"siyuan-note/siyuan","slug":"value-must-not-be-null","errorCode":null,"errorMessage":"value must not be null","messagePattern":"value must not be null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/decode.go","lineNumber":171,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// decodeRequestValue 递归绑定复合参数，避免数组元素和嵌套字段绕过空值及必填检查。\nfunc decodeRequestValue(raw json.RawMessage, value reflect.Value) error {\n\tif value.Type() == reflect.TypeFor[JSONValue]() || value.Type() == reflect.TypeFor[PublishDataValue]() {\n\t\treturn json.Unmarshal(raw, value.Addr().Interface())\n\t}\n\tif bytes.Equal(bytes.TrimSpace(raw), []byte(\"null\")) {\n\t\tswitch value.Kind() {\n\t\tcase reflect.Pointer, reflect.Map, reflect.Slice:\n\t\t\tvalue.SetZero()\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"value must not be null\")\n\t\t}\n\t}\n\tswitch value.Kind() {\n\tcase reflect.Pointer:\n\t\tvalue.Set(reflect.New(value.Type().Elem()))\n\t\treturn decodeRequestValue(raw, value.Elem())\n\tcase reflect.Struct:\n\t\tvar fields map[string]json.RawMessage\n\t\tif err := json.Unmarshal(raw, &fields); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn decodeRequestFields(value, fields)\n\tcase reflect.Slice:\n\t\tvar entries []json.RawMessage\n\t\tif err := json.Unmarshal(raw, &entries); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvalue.Set(reflect.MakeSlice(value.Type(), len(entries), len(entries)))","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/decode.go#L153-L189","documentation":"decodeRequestValue in kernel/apicontract/decode.go rejects a JSON literal null for any destination that is not a pointer, map, or slice. Pointers/maps/slices accept null (set to zero value), but a plain string, number, or bool field cannot represent null, so the decoder fails fast instead of silently coercing. This keeps typed contract fields strictly typed.","triggerScenarios":"Posting a JSON body where a non-pointer scalar contract field (e.g. a string id, bool flag, or number) is explicitly set to null, either literally or via JSON.stringify of an object whose property is null; nested through decodeExportFields/decodeRequestFields.","commonSituations":"JavaScript clients building objects with null placeholders; optional fields represented as null instead of being omitted; older plugin code written against a nullable field that became non-nullable in the contract.","solutions":["Omit the field entirely instead of sending null","Send a sensible zero value (empty string, false, 0) rather than null","Make the field optional in your client by conditionally including it in the payload","Check the contract type in kernel/apicontract/ to see which fields accept null (pointer/map/slice only)"],"exampleFix":"// before\nconst payload = { id: null, title: \"doc\" };\n// after\nconst payload = { title: \"doc\" }; // omit null fields","handlingStrategy":"type-guard","validationCode":"function stripNulls(obj) {\n  return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null));\n}\npayload = stripNulls(payload);","typeGuard":"const isNullableField = (kind) => [\"pointer\", \"map\", \"slice\"].includes(kind); // only these accept null server-side","tryCatchPattern":"try { await fetchPost(url, stripNulls(payload)); } catch (e) { if (String(e).includes(\"must not be null\")) console.error(\"Non-nullable field sent as null:\", e); }","preventionTips":["Omit optional fields instead of sending null","Use JSON.stringify replacers to drop null values","Check contract structs to know which fields accept null","Enable TS strictNullChecks so nulls cannot sneak into payloads"],"tags":["api","json","null","validation"],"backgroundTag":"null-argument","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}