{"record":{"id":"39ca7eee7ea2a13e","repo":"siyuan-note/siyuan","slug":"invalid-plugin-service-response-options","errorCode":null,"errorMessage":"invalid plugin service response options","messagePattern":"invalid plugin service response options","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":125,"sourceCode":"\t\t\tpanic(\"plugin service requires a selected response mode\")\n\t\t}\n\t\treturn 200\n\t}\n\tif err := validatePluginServiceStatus(response.pluginServiceMode, response.httpStatus); err != nil {\n\t\tpanic(err)\n\t}\n\treturn response.httpStatus\n}\n\nfunc 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}","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L107-L143","documentation":"An endpoint declared as a plugin service must use PluginServiceContent as its Data type and must not combine the plugin-service output with any other response options: SSE, Proxy, WebSocket definitions, DataOnError, or a custom ErrorStatus. validatePluginServiceDefinition returns this error when any of these conflicting options are present, because plugin service responses carry their mode/status in private response metadata set by the StreamPluginService-style constructors, not in the shared option fields.","triggerScenarios":"BuildBundle validates a Definition whose Output is PluginServiceOutput but whose Data is a concrete payload type (not reflect.TypeFor[PluginServiceContent]()), or which also sets SSE, Proxy, WebSocket, DataOnError, or ErrorStatus.","commonSituations":"Converting an ordinary JSON endpoint into a plugin service by only changing the Output mode while leaving Data typed and ErrorStatus set, mixing SSE/event features into a plugin service endpoint, or merging definitions from two templates.","solutions":["Set definition.Data to reflect.TypeFor[apicontract.PluginServiceContent]()","Remove SSE, Proxy, WebSocket, DataOnError, and ErrorStatus from the plugin-service definition","If the endpoint needs those other options, it is not a plugin service — use the appropriate output mode instead"],"exampleFix":"// before\ndefinition := apicontract.Definition{Output: apicontract.PluginServiceOutput, Data: reflect.TypeFor[MyPayload](), ErrorStatus: 500, PluginService: &opts.PluginService}\n// after\ndefinition := apicontract.Definition{Output: apicontract.PluginServiceOutput,\n    Data: reflect.TypeFor[apicontract.PluginServiceContent](),\n    PluginService: &opts.PluginService}","handlingStrategy":"validation","validationCode":"if definition.Output == apicontract.PluginServiceOutput {\n    if definition.Data != reflect.TypeFor[apicontract.PluginServiceContent]() ||\n        definition.SSE != nil || definition.Proxy != nil || definition.WebSocket != nil ||\n        definition.DataOnError || definition.ErrorStatus != 0 {\n        return errors.New(\"plugin service definitions must use PluginServiceContent and no other response options\")\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := apicontract.BuildBundle(endpoints); err != nil {\n    if strings.Contains(err.Error(), \"response options\") { log.Fatalf(\"conflicting endpoint options: %v\", err) }\n    return err\n}","preventionTips":["When converting an endpoint to plugin service output, strip SSE/Proxy/WebSocket/DataOnError/ErrorStatus in the same change","Keep plugin service payload typing in the stream callback, not in Data","Add a definition-lint unit test for every endpoint table"],"tags":["bundle-validation","configuration","conflicting-options","plugin-service"],"backgroundTag":"conflicting-config-options","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"}