siyuan-note/siyuan · warning · ErrPluginPublishDenied

plugin publish access denied

Error message

plugin publish access denied

What it means

ErrPluginPublishDenied is returned when access to a plugin's publish-mode resources or data is not permitted. The kernel throws it whenever the package name is invalid, the plugin is not marked accessible in publish mode (CheckPluginAccessableInPublish), the requested resource is not declared in plugin.json's publish declaration, or the publish data fields are not granted. The API layer maps it to HTTP 403 Forbidden.

Solutions

  1. Enable the plugin's accessibility in the publish service settings so CheckPluginAccessableInPublish passes
  2. Declare the file under publish.resources in the plugin's plugin.json (exact file names only, no directories or wildcards; index.js, index.css and i18n/*.json are implicit)
  3. Grant the requested data fields for the plugin in the publish data authorization settings
  4. Fix the package name to satisfy bazaar.IsValidPackageName (lowercase letters, digits, hyphens)
  5. Handle HTTP 403 on the caller side and inspect kernel logs to see which guard failed

Example fix

// before: fetching an undeclared resource
GET /publish/plugins/my-plugin/README.md  -> 403 plugin publish access denied
// after: declare it in plugins/my-plugin/plugin.json
"publish": { "resources": ["README.md"], "data": [] }
Defensive patterns

Strategy: validation

Validate before calling

const validPkg = /^[a-z0-9-]+$/.test(pluginName);
const declared = publishDeclaration.resources.includes(resource) ||
  resource === "index.js" || resource === "index.css" ||
  /^i18n\/[^/]+\.json$/.test(resource);
const accessible = publishServiceSettings.plugins[pluginName]?.accessible;
if (!(validPkg && declared && accessible)) throw new Error("request would be denied (403)");

Prevention

When it happens

Trigger: OpenPluginPublishResource with an undeclared resource or an inaccessible plugin; pluginPublishDeclaration with a name failing bazaar.IsValidPackageName; LoadPluginPublishData when publishFieldsGranted is false; SavePluginPublishData/LoadPluginPublishData for plugins without publish access; accessing plugin.json or kernel.js which are explicitly excluded from declared resources.

Common situations: A frontend/theme requests a plugin file not listed under publish.resources in plugin.json; a plugin name contains invalid characters (path traversal attempts); an operator never enabled the plugin in publish service settings; requesting publish data fields the user did not grant.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/87dca69e0160ea99. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/plugin_publish.go:21

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"os"
	"path/filepath"
	"slices"
	"strings"
	"sync"

	"github.com/88250/gulu"
	"github.com/gin-gonic/gin"
	"github.com/siyuan-note/siyuan/kernel/bazaar"
	"github.com/siyuan-note/siyuan/kernel/util"
)

var (
	ErrPluginPublishDenied  = errors.New("plugin publish access denied")
	ErrPluginPublishMissing = errors.New("plugin publish data has not been generated")
	ErrPluginPublishInvalid = errors.New("invalid plugin publish declaration or data")
	pluginPublishLock       sync.Mutex
)

// PluginPublishDeclaration 的资源为精确文件名,数据为可公开的顶层标量字段,不支持目录或通配符。
type PluginPublishDeclaration struct {
	Resources []string `json:"resources"`
	Data      []string `json:"data"`
}

type PluginPublishInfo struct {
	Resources []string `json:"resources"`
	Fields    []string `json:"fields"`
	Granted   bool     `json:"granted"`
}

type pluginPublishState struct {

View on GitHub (pinned to 9f775e8a12)