{"record":{"id":"58bf2b3155d72e9f","repo":"siyuan-note/siyuan","slug":"field-conf-is-required-graph-query","errorCode":null,"errorMessage":"Field [conf] is required","messagePattern":"Field \\[conf\\] is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/graph_query.go","lineNumber":114,"sourceCode":"func (e *graphQueryDecodeError) Error() string { return e.cause.Error() }\n\nfunc graphQueryFields(reader io.Reader, path string) (map[string]json.RawMessage, JSONValue, error) {\n\tfields, err := blockRequestFields(reader, path)\n\tvar reqID JSONValue\n\tif err != nil {\n\t\treturn nil, reqID, err\n\t}\n\tif raw, exists := fields[\"reqId\"]; exists {\n\t\tif err = json.Unmarshal(raw, &reqID); err != nil {\n\t\t\treturn nil, reqID, err\n\t\t}\n\t}\n\treturn fields, reqID, nil\n}\n\nfunc decodeGraphConfiguration(fields map[string]json.RawMessage) (value GraphConfiguration, err error) {\n\tif len(fields[\"conf\"]) == 0 {\n\t\treturn value, fmt.Errorf(\"Field [conf] is required\")\n\t}\n\terr = json.Unmarshal(fields[\"conf\"], &value)\n\treturn\n}\n\nfunc init() {\n\tGetGraph.decodeRequest = func(reader io.Reader) (request GlobalGraphRequest, err error) {\n\t\tfields, reqID, err := graphQueryFields(reader, \"/api/graph/getGraph\")\n\t\tif err != nil {\n\t\t\treturn request, err\n\t\t}\n\t\trequest.ReqID = reqID\n\t\tdefer func() {\n\t\t\tif err != nil {\n\t\t\t\terr = &graphQueryDecodeError{reqID, err}\n\t\t\t}\n\t\t}()\n\t\tif request.K, err = legacyField[string](fields, \"k\", \"String\", false); err != nil {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/graph_query.go#L96-L132","documentation":"decodeGraphConfiguration decodes the `conf` entry of an incoming fields map into a GraphConfiguration value. When the `conf` key is absent or null (zero-length raw bytes) it returns 'Field [conf] is required'. This is the query-side mirror of the write-path validation, ensuring graph queries that return configuration always carry a conf object.","triggerScenarios":"A graph query request (decoded in graph_query.go) whose payload omits `conf` or sets it to null; decodeGraphConfiguration is invoked from the anonymous request-decoding function for query endpoints.","commonSituations":"Query clients sending only identifiers (like `id`) and forgetting that configuration is also required; template-based requests where the conf section was deleted.","solutions":["Include \"conf\": {} (or the actual configuration object) in the query payload","Replace \"conf\": null with a concrete object","Inspect the request body actually sent (log it) — a serialization bug may drop empty objects"],"exampleFix":"// before\n{\"id\": \"local\"}\n// after\n{\"id\": \"local\", \"conf\": {}}","handlingStrategy":"validation","validationCode":"if (payload.conf == null) {\n  payload.conf = {}; // ensure query payloads always carry conf\n}","typeGuard":"const hasConfField = (b) => Object.prototype.hasOwnProperty.call(b, \"conf\") && b.conf !== null;","tryCatchPattern":null,"preventionTips":["Build graph query payloads through a helper that always injects conf","Avoid deleting conf from shared request templates"],"tags":["api","validation","missing-field","query"],"backgroundTag":"missing-required-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"}