{"record":{"id":"6348082136b8bffa","repo":"siyuan-note/siyuan","slug":"field-s-is-required-decode","errorCode":null,"errorMessage":"Field [%s] is required","messagePattern":"Field \\[(.+?)\\] is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/decode.go","lineNumber":73,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\tif field.Anonymous && name == \"\" {\n\t\t\tif field.Type.Kind() != reflect.Struct {\n\t\t\t\treturn fmt.Errorf(\"unsupported embedded request field: %s\", field.Name)\n\t\t\t}\n\t\t\tif err := decodeRequestFields(value.Field(i), fields); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\toptions := \",\" + field.Tag.Get(\"api\") + \",\"\n\t\thas := func(option string) bool { return strings.Contains(options, \",\"+option+\",\") }\n\t\traw, present := fields[name]\n\t\tif !present {\n\t\t\tif has(\"optional\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"Field [%s] is required\", name)\n\t\t}\n\t\tisNull := bytes.Equal(bytes.TrimSpace(raw), []byte(\"null\"))\n\t\tif isNull && !has(\"nullable\") && field.Type.Kind() != reflect.Pointer && !has(\"filterstrings\") {\n\t\t\treturn fmt.Errorf(\"Field [%s] must not be null\", name)\n\t\t}\n\t\tif has(\"filterstrings\") {\n\t\t\tvar entries []json.RawMessage\n\t\t\tif json.Unmarshal(raw, &entries) == nil {\n\t\t\t\tvar ids []string\n\t\t\t\tfor _, entry := range entries {\n\t\t\t\t\tvar id string\n\t\t\t\t\tif !bytes.Equal(bytes.TrimSpace(entry), []byte(\"null\")) && json.Unmarshal(entry, &id) == nil {\n\t\t\t\t\t\tids = append(ids, id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvalue.Field(i).Set(reflect.ValueOf(ids))\n\t\t\t}\n\t\t\tcontinue","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/decode.go#L55-L91","documentation":"A request field whose json tag does not carry the 'optional' api option must be present in the decoded body. If the key is missing from the JSON object entirely, Decode rejects the request naming the field. This enforces required fields declared by the contract.","triggerScenarios":"POSTing to a contract endpoint while omitting a field tagged `api:\"...\"` without 'optional' — e.g. missing \"id\" in a block-update request or missing \"path\" in a file-tree operation.","commonSituations":"Older clients not sending fields added in newer contract versions; frontend code constructing partial payloads; plugin authors guessing at the request shape; null being sent when the field must be a concrete value (that would hit must-not-be-null instead).","solutions":["Add the missing field with a valid value to the request body","If the field is genuinely optional, add `optional` to its api tag in the contract","Check the endpoint's contract definition (or generated schema) for the exact required field name and type"],"exampleFix":"// before\nfetchPost(\"/api/filetree/renameDoc\", {notebook: nb})\n// after\nfetchPost(\"/api/filetree/renameDoc\", {notebook: nb, path: \"/20240101120000-abc.sy\"})","handlingStrategy":"validation","validationCode":"function requireFields(body, required) {\n  const missing = required.filter(k => !(k in body))\n  if (missing.length) throw new Error(`Missing required fields: ${missing.join(\", \")}`)\n  return true\n}\n// requireFields(payload, [\"id\", \"path\"]) before calling the endpoint","typeGuard":"function hasField<T, K extends keyof T>(obj: T, key: K): obj is T & Required<Pick<T, K>> { return key in obj }","tryCatchPattern":"try { await fetchPost(path, payload) } catch (e) { const m = e.message.match(/Field \\[(.+?)\\] is required/); if (m) { console.error(`Add required field \"${m[1]}\" to request for ${path}`); return } throw e }","preventionTips":["Consult the endpoint's generated schema for required fields","Use typed clients so missing fields fail at compile time","Send all fields explicitly rather than relying on defaults","When a field is genuinely optional, mark it `optional` in the contract"],"tags":["validation","api-contract","required-field","http"],"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"}