{"record":{"id":"47e603ad30b73f8d","repo":"siyuan-note/siyuan","slug":"unknown-plugin-service-mode-s","errorCode":null,"errorMessage":"unknown plugin service mode: %s","messagePattern":"unknown plugin service mode: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":145,"sourceCode":"\tif !reflect.DeepEqual(definition.PluginService, PluginServiceOptions().PluginService) {\n\t\treturn fmt.Errorf(\"invalid plugin service protocol variants\")\n\t}\n\treturn nil\n}\n\nfunc validatePluginServiceStatus(mode PluginServiceMode, status int) error {\n\tif status < 100 || status > 999 {\n\t\treturn fmt.Errorf(\"invalid plugin service HTTP status: %d\", status)\n\t}\n\tknown := false\n\tfor _, variant := range PluginServiceOptions().PluginService.Variants {\n\t\tif variant.Mode == mode {\n\t\t\tknown = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !known {\n\t\treturn fmt.Errorf(\"unknown plugin service mode: %s\", mode)\n\t}\n\tswitch mode {\n\tcase PluginServiceAdmission:\n\t\tif status != 400 && status != 404 && status != 500 && status != 503 {\n\t\t\treturn fmt.Errorf(\"undeclared plugin admission status\")\n\t\t}\n\tcase PluginServiceRedirect:\n\t\tif status != 201 && (status < 300 || status > 308) {\n\t\t\treturn fmt.Errorf(\"invalid plugin redirect status\")\n\t\t}\n\tcase PluginServiceWebSocket:\n\t\tif status != 101 && status != 400 && status != 500 {\n\t\t\treturn fmt.Errorf(\"invalid plugin WebSocket status\")\n\t\t}\n\tcase PluginServiceSSE:\n\t\tif status != 200 && status != 500 {\n\t\t\treturn fmt.Errorf(\"invalid plugin SSE status\")\n\t\t}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L127-L163","documentation":"validatePluginServiceStatus checks that the response mode passed to a plugin-service endpoint is one of the finite set of registered variants (JSON, JSONP, file, redirect, proxy, websocket, sse, admission, etc.). This error means the mode string did not match any variant, so the kernel refuses to describe or validate a response for it. It surfaces when building/validating the endpoint schema via StreamPluginService, pluginServiceStatus, or ValidatePluginServiceResponse.","triggerScenarios":"Calling StreamPluginService (or returning a Response) with a PluginServiceMode value that is not one of the 19 constants in plugin_service_protocol.go (e.g. a custom mode like \"html\" or an empty string), or invoking Bundle.ValidatePluginServiceResponse with a mode not in the registered variant list.","commonSituations":"Plugin authors hand-write a mode string instead of using the exported PluginService* constants; a typo or casing mismatch (\"json\" vs \"JSON\"); a plugin built against a newer/older apicontract version that added or renamed a variant.","solutions":["Use one of the exported constants (PluginServiceJSON, PluginServiceFile, PluginServiceRedirect, ...) instead of a literal string","Check the mode value for typos and exact casing (modes are compared with ==, e.g. \"JSON\" not \"json\")","Rebuild the plugin against the current kernel/apicontract version so its mode set matches the registered variants"],"exampleFix":"// before\nStreamPluginService(\"json\", 200, serve)\n// after\nStreamPluginService(PluginServiceJSON, 200, serve)","handlingStrategy":"validation","validationCode":"func isKnownPluginServiceMode(mode apicontract.PluginServiceMode) bool {\n\tfor _, v := range apicontract.PluginServiceOptions().PluginService.Variants {\n\t\tif v.Mode == mode {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"// StreamPluginService panics on invalid mode; recover at route-registration time:\nfunc safeStream(mode apicontract.PluginServiceMode, status int, serve func(http.ResponseWriter, *http.Request)) (r apicontract.Response[apicontract.PluginServiceContent]) {\n\tdefer func() {\n\t\tif rec := recover(); rec != nil {\n\t\t\tlog.Printf(\"plugin service mode rejected: %v\", rec)\n\t\t}\n\t}()\n\treturn apicontract.StreamPluginService(mode, status, serve)\n}","preventionTips":["Always use the exported PluginService* constants, never string literals","Add a unit test that validates every mode your plugin uses via Bundle.ValidatePluginServiceResponse","Keep the plugin's apicontract dependency version in sync with the kernel"],"tags":["go","api-contract","plugin-service","validation"],"backgroundTag":"invalid-enum-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"}