{"record":{"id":"2488b4190f738c3e","repo":"siyuan-note/siyuan","slug":"undeclared-plugin-admission-status","errorCode":null,"errorMessage":"undeclared plugin admission status","messagePattern":"undeclared plugin admission status","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":150,"sourceCode":"\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}\n\t}\n\treturn nil\n}\n\nfunc (b *Bundle) validatePluginServiceHTTPResponse(endpoint EndpointSchema, status int, contentType string, payload []byte) error {","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L132-L168","documentation":"When a plugin-service variant declares admission mode, the HTTP status must be one of the statuses registered in the plugin-service definition's AdmissionStatuses (400, 404, 500, 503 per PluginServiceOptions). This error means validatePluginServiceStatus saw admission mode with a status outside that declared set, so the admission response is not contract-conformant.","triggerScenarios":"Calling StreamPluginService(PluginServiceAdmission, status, serve) or Bundle.ValidatePluginServiceResponse(..., PluginServiceAdmission, status, ...) with any status other than 400, 404, 500, or 503 (e.g. 200 or 401).","commonSituations":"Plugin authors want to reject a request with a custom admission code like 403 or 422; they return a success status (200) from an admission gate by mistake; the admission statuses list was changed in the contract but the plugin still sends old codes.","solutions":["Return only one of the declared admission statuses: 400, 404, 500, or 503","If the desired rejection code differs, use a different variant (e.g. PluginServiceJSON with that status) instead of admission mode","If the contract itself should allow the status, update the AdmissionStatuses declaration in PluginServiceOptions and the validation switch together"],"exampleFix":"// before\nStreamPluginService(PluginServiceAdmission, 403, serve)\n// after\nStreamPluginService(PluginServiceAdmission, 400, serve)","handlingStrategy":"validation","validationCode":"allowed := []int{400, 404, 500, 503}\nif mode == apicontract.PluginServiceAdmission && !slices.Contains(allowed, status) {\n\treturn fmt.Errorf(\"admission status %d not in %v\", status, allowed)\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif rec := recover(); rec != nil {\n\t\tlog.Printf(\"invalid admission status: %v\", rec)\n\t}\n}() // around StreamPluginService(PluginServiceAdmission, ...)","preventionTips":["Restrict admission responses to 400/404/500/503","Use admission mode only for gate/reject decisions; use other variants for success paths","Check the AdmissionStatuses field in PluginServiceOptions before adding new codes"],"tags":["go","api-contract","plugin-service","http-status"],"backgroundTag":"unexpected-http-status","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"}