{"record":{"id":"21a2cbaaffd9e5e4","repo":"siyuan-note/siyuan","slug":"published-data-fields-must-be-json-scalars","errorCode":null,"errorMessage":"published data fields must be JSON scalars","messagePattern":"published data fields must be JSON scalars","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_publish.go","lineNumber":21,"sourceCode":"import (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n)\n\n// PublishDataValue 限定公开字段为标量，避免嵌套对象扩展绕过字段授权。\ntype PublishDataValue struct{ raw json.RawMessage }\n\nfunc (v PublishDataValue) MarshalJSON() ([]byte, error) {\n\tif len(v.raw) == 0 {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn v.raw, nil\n}\nfunc (v *PublishDataValue) UnmarshalJSON(data []byte) error {\n\tdata = bytes.TrimSpace(data)\n\tif !json.Valid(data) || data[0] == '{' || data[0] == '[' {\n\t\treturn errors.New(\"published data fields must be JSON scalars\")\n\t}\n\tv.raw = append(v.raw[:0], data...)\n\treturn nil\n}\n\ntype PluginPublishRequest struct {\n\tPackageName string `json:\"packageName\"`\n}\n\ntype PluginPublishInfo struct {\n\tResources []string `json:\"resources\" api:\"nonnullable\"`\n\tFields    []string `json:\"fields\" api:\"nonnullable\"`\n\tGranted   bool     `json:\"granted\"`\n}\n\ntype SetPluginPublishDataGrantRequest struct {\n\tPackageName string   `json:\"packageName\"`\n\tFields      []string `json:\"fields\"`","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_publish.go#L3-L39","documentation":"PublishDataValue is a JSON value type used by the plugin publish request to ensure each published data field is a JSON scalar (string, number, boolean, null). UnmarshalJSON rejects empty/invalid JSON as well as any value starting with '{' or '[' — i.e. objects and arrays — to keep published data flat and scalar-only.","triggerScenarios":"Unmarshalling a plugin publish request whose data object contains a field whose value is a JSON object or array (e.g. {\"data\": {\"key\": {\"nested\": 1}}} or {\"key\": [1,2]}).","commonSituations":"A plugin publishes structured/nested metadata instead of flat scalar key-values; a client reuses a generic config object as the data payload; schema drift after adding nested fields.","solutions":["Flatten the published data so every field value is a scalar (string, number, boolean, or null).","Serialize nested structures to a JSON string inside a scalar field if structure must be preserved.","Validate the payload with json.Valid plus a scalar check before sending."],"exampleFix":"// before\n{\"data\": {\"meta\": {\"version\": 1}}}\n// after\n{\"data\": {\"meta\": \"{\\\"version\\\":1}\"}}","handlingStrategy":"validation","validationCode":"function dataFieldsAreScalars(data) {\n  return Object.values(data).every(v => v === null || [\"string\", \"number\", \"boolean\"].includes(typeof v));\n}","typeGuard":"const isScalar = (v) => v === null || [\"string\", \"number\", \"boolean\"].includes(typeof v);","tryCatchPattern":"try { await publish({data}); } catch (e) { if (String(e).includes(\"must be JSON scalars\")) { throw new Error(\"flatten data fields before publishing: \" + e.message); } throw e; }","preventionTips":["Keep published plugin data flat: scalar values only.","JSON.stringify nested objects into a string field when structure is needed.","Add a payload unit test that validates each data field type before release."],"tags":["json","validation","plugin"],"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"}