{"record":{"id":"4fea9ec958f5410a","repo":"siyuan-note/siyuan","slug":"field-keyids-must-not-be-null","errorCode":null,"errorMessage":"Field [keyIDs] must not be null","messagePattern":"Field \\[keyIDs\\] must not be null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/av_decode.go","lineNumber":82,"sourceCode":"func init() {\n\tGetAttributeViewRowSort.decodeRequest = func(reader io.Reader) (request GetAttributeViewRowSortRequest, err error) {\n\t\terr = json.NewDecoder(reader).Decode(&request)\n\t\treturn request, err\n\t}\n\tGetAttributeViewKeysByID.decodeRequest = func(reader io.Reader) (request GetAttributeViewKeysByIDRequest, err error) {\n\t\tfields, err := blockRequestFields(reader, \"/api/av/getAttributeViewKeysByID\")\n\t\tif err != nil {\n\t\t\treturn request, err\n\t\t}\n\t\traw, present := fields[\"keyIDs\"]\n\t\tfields[\"keyIDs\"] = json.RawMessage(\"[]\")\n\t\tif err = decodeRequestFields(reflect.ValueOf(&request).Elem(), fields); err != nil {\n\t\t\treturn request, err\n\t\t}\n\t\tif !present {\n\t\t\trequest.KeyIDsError = fmt.Errorf(\"Field [keyIDs] is required\")\n\t\t} else if bytes.Equal(bytes.TrimSpace(raw), []byte(\"null\")) {\n\t\t\trequest.KeyIDsError = fmt.Errorf(\"Field [keyIDs] must not be null\")\n\t\t} else {\n\t\t\trequest.KeyIDsError = decodeRequestValue(raw, reflect.ValueOf(&request.KeyIDs).Elem())\n\t\t}\n\t\treturn request, nil\n\t}\n\tGetAttributeViewPasteRows.decodeRequest = func(reader io.Reader) (GetAttributeViewPasteRowsRequest, error) {\n\t\treturn avDecodeParsedRequest[GetAttributeViewPasteRowsRequest](reader, \"/api/av/getAttributeViewPasteRows\")\n\t}\n\tGetAttributeViewFieldViews.decodeRequest = func(reader io.Reader) (GetAttributeViewFieldViewsRequest, error) {\n\t\treturn avDecodeParsedRequest[GetAttributeViewFieldViewsRequest](reader, \"/api/av/getAttributeViewFieldViews\")\n\t}\n\tCreateAttributeViewItem.decodeRequest = func(reader io.Reader) (CreateAttributeViewItemRequest, error) {\n\t\treturn avDecodeParsedRequest[CreateAttributeViewItemRequest](reader, \"/api/av/createAttributeViewItem\")\n\t}\n\tCreateAttributeViewItemWithMarkdown.decodeRequest = func(reader io.Reader) (CreateAttributeViewItemWithMarkdownRequest, error) {\n\t\treturn avDecodeParsedRequest[CreateAttributeViewItemWithMarkdownRequest](reader, \"/api/av/createAttributeViewItemWithMarkdown\")\n\t}\n\tCreateAttributeViewItemDocs.decodeRequest = func(reader io.Reader) (CreateAttributeViewItemDocsRequest, error) {","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/av_decode.go#L64-L100","documentation":"Same lenient keyIDs decoding as the 'is required' case, but for an explicitly null value: when 'keyIDs' is present yet JSON null, GetAttributeViewKeysByIDRequest.KeyIDsError is set to 'Field [keyIDs] must not be null'. Null cannot be decoded into the key-ID string slice.","triggerScenarios":"POST /api/av/getAttributeViewKeysByID with {\"keyIDs\": null}.","commonSituations":"Client code doing keyIDs: someVar where someVar is undefined/null, serializers emitting null for empty lists, template interpolation producing null.","solutions":["Replace null with an empty array [] if no specific keys are needed","Default the variable before sending: keyIDs = keyIDs ?? []","Handle the KeyIDsError surfaced by the endpoint response"],"exampleFix":"// before\nfetchPost(\"/api/av/getAttributeViewKeysByID\", {avID, keyIDs: null});\n// after\nfetchPost(\"/api/av/getAttributeViewKeysByID\", {avID, keyIDs: keyIDs ?? []});","handlingStrategy":"validation","validationCode":"payload.keyIDs = payload.keyIDs ?? [];","typeGuard":"const isStringArray = (v) => Array.isArray(v) && v.every((x) => typeof x === \"string\");","tryCatchPattern":"try { const res = await post(\"/api/av/getAttributeViewKeysByID\", payload); if (res.keyIDsError) handle(res.keyIDsError); } catch (e) { /* ... */ }","preventionTips":["Avoid `keyIDs: maybeUndefined` shorthand; use explicit defaults","Check for null after JSON.parse of stored settings","Write a payload builder that guarantees array-typed fields"],"tags":["validation","api","null-value"],"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"}