{"record":{"id":"1fe23e789a2b3f9e","repo":"siyuan-note/siyuan","slug":"invalid-plugin-service-http-status-d","errorCode":null,"errorMessage":"invalid plugin service HTTP status: %d","messagePattern":"invalid plugin service HTTP status: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":135,"sourceCode":"func validatePluginServiceDefinition(definition Definition) error {\n\tif (definition.Output == PluginServiceOutput) != (definition.PluginService != nil) {\n\t\treturn fmt.Errorf(\"plugin service output requires a protocol declaration\")\n\t}\n\tif definition.PluginService == nil {\n\t\treturn nil\n\t}\n\tif definition.Data != reflect.TypeFor[PluginServiceContent]() || definition.SSE != nil || definition.Proxy != nil || definition.WebSocket != nil || definition.DataOnError || definition.ErrorStatus != 0 {\n\t\treturn fmt.Errorf(\"invalid plugin service response options\")\n\t}\n\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) {","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L117-L153","documentation":"validatePluginServiceStatus checks that a plugin service response status is a valid HTTP code (100-999) and also that it is permitted for the selected PluginServiceMode. This specific error fires when the status integer falls outside 100-999 (zero or negative values already normalized to 200 by StreamPluginService, so this typically comes from a raw, unvalidated status). Callers include StreamPluginService (panics), pluginServiceStatus, and ValidatePluginServiceResponse.","triggerScenarios":"StreamPluginService(mode, status, serve) is called with a status < 100 or > 999; a Response[PluginServiceContent] carries an out-of-range httpStatus when the endpoint computes pluginServiceStatus; ValidatePluginServiceResponse is invoked with a bad status during bundle response validation.","commonSituations":"Passing an error-sentinel status like -1 or 0 from application code that assumed the library would substitute a default (only 0/negative is normalized, but custom paths may bypass), computing a status arithmetically (e.g. 200+extra*1000), or forwarding an upstream status code larger than 999.","solutions":["Pass a valid HTTP status (100-999) to StreamPluginService; omit it (or pass <= 0) to get the 200 default","Clamp or validate computed status values before constructing the response","Check upstream proxies/forwarded codes and map anything outside 100-999 to a standard status"],"exampleFix":"// before\nstatus := baseStatus + delta*1000\nresp := apicontract.StreamPluginService(apicontract.PluginServiceJSON, status, serve)\n// after\nif status < 100 || status > 999 {\n    status = http.StatusInternalServerError\n}\nresp := apicontract.StreamPluginService(apicontract.PluginServiceJSON, status, serve)","handlingStrategy":"validation","validationCode":"func validStatus(s int) bool { return s >= 100 && s <= 999 }\nif !validStatus(status) { status = http.StatusInternalServerError }","typeGuard":null,"tryCatchPattern":"func serve(mode apicontract.PluginServiceMode, status int) (r apicontract.Response[apicontract.PluginServiceContent]) {\n    defer func() { if rec := recover(); rec != nil { log.Printf(\"status rejected: %v\", rec); r = fallback(mode) } }()\n    return apicontract.StreamPluginService(mode, status, handler)\n}","preventionTips":["Use net/http status constants instead of raw arithmetic on codes","Rely on StreamPluginService's <=0 default (200) instead of sentinel values","Validate any status forwarded from upstream services before mapping it into a plugin service response"],"tags":["http","status-code","validation","plugin-service"],"backgroundTag":"value-out-of-range","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"}