{"record":{"id":"5b865552e01da13d","repo":"siyuan-note/siyuan","slug":"field-conf-should-be-of-type-object","errorCode":null,"errorMessage":"Field [conf] should be of type [Object]","messagePattern":"Field \\[conf\\] should be of type \\[Object\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/graph_input.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\n// GraphConfiguration 延迟绑定具体图配置，使局部图继续忽略全局图字段，并保留默认值合并顺序。\ntype GraphConfiguration struct{ raw json.RawMessage }\n\nfunc (c *GraphConfiguration) UnmarshalJSON(data []byte) error {\n\tif bytes.Equal(bytes.TrimSpace(data), []byte(\"null\")) {\n\t\treturn fmt.Errorf(\"Field [conf] is required\")\n\t}\n\tfields, err := legacyJSONValue[map[string]json.RawMessage](data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Field [conf] should be of type [Object]\")\n\t}\n\tc.raw, err = json.Marshal(fields)\n\treturn err\n}\n\nfunc (c GraphConfiguration) MarshalJSON() ([]byte, error) {\n\tif len(c.raw) == 0 {\n\t\treturn []byte(\"{}\"), nil\n\t}\n\treturn c.raw, nil\n}\n\ntype GraphConfigurationFields struct {\n\tMinRefs   *int             `json:\"minRefs\" api:\"optional\"`\n\tDailyNote *bool            `json:\"dailyNote\" api:\"optional\"`\n\tType      *GraphTypeFilter `json:\"type\" api:\"optional\"`\n\tD3        *GraphD3         `json:\"d3\" api:\"optional\"`\n}","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/graph_input.go#L2-L38","documentation":"GraphConfiguration.UnmarshalJSON rejects any payload whose top-level JSON value is not a JSON object. The field being decoded is named `conf`, so when the raw bytes cannot be decoded into map[string]json.RawMessage (e.g. the caller sent a string, number, array, or malformed JSON), the decoder reports that `conf` must be an Object. This keeps the graph configuration contract strict: configuration is always a keyed object, never a scalar or list.","triggerScenarios":"Posting a graph configuration (e.g. to the graph-related kernel endpoints, or decoding a payload containing `conf`) where the `conf` value is a JSON string, number, array, boolean, or the whole body is not an object; also thrown for `null` only with a different message, so this one specifically means a non-object non-null value or syntactically invalid JSON.","commonSituations":"Clients quoting the config object as a string (double-encoded JSON), sending `[]` instead of `{}` as an empty config, or hand-built request bodies with a typo that breaks JSON parsing.","solutions":["Send `conf` as a JSON object, e.g. {\"conf\": {\"key\": \"value\"}}","If you serialized the config in your client, make sure it was not JSON.stringify-ed twice (string instead of object)","Validate the request body parses as JSON before sending","Use {} for an empty graph configuration instead of [] or \"\""],"exampleFix":"// before\n{\"conf\": \"{\\\"local\\\":\\\"true\\\"}\"}\n// after\n{\"conf\": {\"local\": \"true\"}}","handlingStrategy":"validation","validationCode":"const conf = body.conf;\nif (typeof conf !== \"object\" || conf === null || Array.isArray(conf)) {\n  throw new Error(\"conf must be a plain JSON object\");\n}","typeGuard":"const isPlainObject = (v) => typeof v === \"object\" && v !== null && !Array.isArray(v);","tryCatchPattern":null,"preventionTips":["Never double-serialize config objects with JSON.stringify before embedding them in a payload","Use {} for empty configs","Lint request-building code so conf is always an object literal"],"tags":["api","json","validation","input"],"backgroundTag":"schema-validation-failed","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"}