{"record":{"id":"3143cb915265bcd2","repo":"siyuan-note/siyuan","slug":"invalid-plugin-publish-declaration-or-data","errorCode":null,"errorMessage":"invalid plugin publish declaration or data","messagePattern":"invalid plugin publish declaration or data","errorType":"exception","errorClass":"ErrPluginPublishInvalid","httpStatus":null,"severity":"error","filePath":"kernel/model/plugin_publish.go","lineNumber":23,"sourceCode":"\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/siyuan-note/siyuan/kernel/bazaar\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\nvar (\n\tErrPluginPublishDenied  = errors.New(\"plugin publish access denied\")\n\tErrPluginPublishMissing = errors.New(\"plugin publish data has not been generated\")\n\tErrPluginPublishInvalid = errors.New(\"invalid plugin publish declaration or data\")\n\tpluginPublishLock       sync.Mutex\n)\n\n// PluginPublishDeclaration 的资源为精确文件名，数据为可公开的顶层标量字段，不支持目录或通配符。\ntype PluginPublishDeclaration struct {\n\tResources []string `json:\"resources\"`\n\tData      []string `json:\"data\"`\n}\n\ntype PluginPublishInfo struct {\n\tResources []string `json:\"resources\"`\n\tFields    []string `json:\"fields\"`\n\tGranted   bool     `json:\"granted\"`\n}\n\ntype pluginPublishState struct {\n\tVersion int                        `json:\"version\"`\n\tGranted []string                   `json:\"granted\"`","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/plugin_publish.go#L5-L41","documentation":"ErrPluginPublishInvalid is thrown when a plugin's publish declaration in plugin.json or its persisted publish data fails validation: manifest >1 MiB, unparseable JSON, manifest.Name mismatch, missing/invalid publish declaration, resource or data field counts over limits (4096/128), undeclared-safe relative paths, reserved files (plugin.json/kernel.js), or data field names outside [A-Za-z0-9_-] or longer than 128 chars. The API layer maps it to HTTP 400 Bad Request.","triggerScenarios":"pluginPublishDeclaration reading a plugin.json over 1 MiB or with a Name not matching the directory; declaring >4096 resources or >128 data fields; a resource path failing util.IsPublishRelativePath or equal (case-insensitive) to plugin.json/kernel.js; data field names with illegal characters; readPluginPublishState/SetPluginPublishDataGrant/SavePluginPublishData encountering corrupt state JSON.","commonSituations":"Hand-edited plugin.json with typo'd publish arrays; wildcard or directory entries in publish.resources (not supported); renaming the plugin directory without updating manifest.Name; corrupted publish state file after a crash or manual edit; data fields created programmatically with spaces or slashes in names.","solutions":["Fix plugins/<name>/plugin.json: make Name match the package, keep publish.resources as exact relative file paths, cap at 4096 resources / 128 data fields","Rename data fields to only [A-Za-z0-9_-] and at most 128 characters","Ensure the file is valid JSON under 1 MiB","Delete the corrupt publish state file for the plugin so it is regenerated, then re-grant and re-save data","Never list plugin.json or kernel.js in resources — they are implicitly forbidden"],"exampleFix":"// before (invalid: directory + reserved file)\n\"publish\": { \"resources\": [\"assets/\", \"plugin.json\"], \"data\": [\"my field\"] }\n// after (valid: exact files, safe field name)\n\"publish\": { \"resources\": [\"assets/logo.png\"], \"data\": [\"my_field\"] }","handlingStrategy":"validation","validationCode":"function validatePublish(manifest, pkgName) {\n  if (manifest.name !== pkgName) return \"manifest.Name must match package name\";\n  const p = manifest.publish || {resources: [], data: []};\n  if (p.resources.length > 4096 || p.data.length > 128) return \"limit exceeded\";\n  for (const r of p.resources) {\n    if (r.includes(\"..\") || r.startsWith(\"/\") || /^(plugin|kernel)\\.json$/i.test(r)) return \"bad resource: \" + r;\n  }\n  for (const f of p.data) {\n    if (!/^[A-Za-z0-9_-]{1,128}$/.test(f)) return \"bad data field: \" + f;\n  }\n  return null;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate plugin.json publish declaration in CI before releasing the plugin","Never use directories, wildcards, or reserved file names in publish.resources","Keep data field names to [A-Za-z0-9_-] and <=128 chars","Keep plugin.json under 1 MiB and confirm Name equals the package directory name"],"tags":["validation","plugin","publish","schema"],"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"}