{"record":{"id":"ee3f70e7c33d7458","repo":"siyuan-note/siyuan","slug":"field-s-has-an-invalid-constant","errorCode":null,"errorMessage":"Field [%s] has an invalid constant","messagePattern":"Field \\[(.+?)\\] has an invalid constant","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/decode.go","lineNumber":152,"sourceCode":"\t\t\t\t}\n\t\t\t\tif !found {\n\t\t\t\t\treturn fmt.Errorf(\"Field [%s] has an invalid value\", name)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif strings.HasPrefix(option, \"const=\") {\n\t\t\t\tvar expected, actual any\n\t\t\t\tif err := json.Unmarshal([]byte(strings.TrimPrefix(option, \"const=\")), &expected); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdata, err := json.Marshal(value.Field(i).Interface())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif err := json.Unmarshal(data, &actual); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(expected, actual) {\n\t\t\t\t\treturn fmt.Errorf(\"Field [%s] has an invalid constant\", name)\n\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:","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/decode.go#L134-L170","documentation":"This error comes from the SiYuan kernel's strict request decoder in kernel/apicontract/decode.go (decodeRequestFields). When a contract field is declared with a fixed constant (a pinned value), the decoder unmarshals the submitted value and compares it with the declared constant using reflect.DeepEqual; any difference means the client sent a value that violates the API contract. It protects endpoints whose fields must equal a specific constant and cannot be freely chosen by the caller.","triggerScenarios":"Calling a kernel API whose contract pins a field to a constant (via contract field metadata) and sending any other value for that field; e.g. passing a different string, number, or object to a field declared as a constant in kernel/apicontract/ definitions decoded by Decode/decodeRequestFields/avDecodeBoundFields/fileTreeBind.","commonSituations":"Hand-crafted fetch/curl requests built from outdated API docs; plugins hard-coding payloads copied from an older SiYuan version after the contract changed; copy-pasted request bodies where one field was edited but a constant field was accidentally changed too.","solutions":["Read the endpoint's contract definition in kernel/apicontract/ and set the field exactly to the declared constant value","Remove the field from your payload if the contract marks it as a constant that the server injects itself","Update your client/plugin code to match the current SiYuan API version's contract","If you believe the constant is wrong, file an issue instead of bypassing the check"],"exampleFix":"// before\nfetchPost(\"/api/filetree/copyDoc\", {path: p, title: t, \"notebook\": \"wrong-id-constant\"})\n// after\nfetchPost(\"/api/filetree/copyDoc\", {path: p, title: t, notebook: NOTEBOOK_ID_CONSTANT})","handlingStrategy":"validation","validationCode":"function assertConstants(payload, constants) {\n  for (const [name, expected] of Object.entries(constants)) {\n    if (JSON.stringify(payload[name]) !== JSON.stringify(expected)) {\n      throw new Error(`Field [${name}] must equal ${JSON.stringify(expected)}`);\n    }\n  }\n}\nassertConstants(payload, { notebook: NOTEBOOK_ID_CONSTANT });","typeGuard":"const isConstantMatch = (v, c) => JSON.stringify(v) === JSON.stringify(c);","tryCatchPattern":null,"preventionTips":["Copy contract constants from the apicontract source rather than retyping them","Pin your client to a SiYuan version and re-check constants on upgrades","Keep payload builders in one module so constant fields are set in one place","Never hand-edit constant fields in saved request templates"],"tags":["api","validation","contract","json"],"backgroundTag":"invalid-argument-value","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"}