{"record":{"id":"7d80f8f51686cd09","repo":"siyuan-note/siyuan","slug":"plugin-service-output-requires-a-protocol-declaration","errorCode":null,"errorMessage":"plugin service output requires a protocol declaration","messagePattern":"plugin service output requires a protocol declaration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":119,"sourceCode":"func (e Endpoint[Request, Data]) pluginServiceStatus(response Response[Data]) int {\n\tif e.definition.Output != PluginServiceOutput || e.definition.PluginService == nil {\n\t\tpanic(\"endpoint does not declare plugin service output\")\n\t}\n\tif response.stream == nil {\n\t\tif response.code == 0 {\n\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","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L101-L137","documentation":"Endpoint definitions that declare Output = PluginServiceOutput must also supply a PluginService protocol declaration (the PluginServiceDefinition returned by PluginServiceOptions()); the two must be set or unset together. validatePluginServiceDefinition, run during bundle building (BuildBundle) and its tests, returns this error when the output mode says \"pluginService\" but the PluginService field is nil, or vice versa.","triggerScenarios":"BuildBundle registers an endpoint whose Definition has Output set to PluginServiceOutput without assigning the PluginService definition, or a normal (non-plugin-service) endpoint that accidentally sets definition.PluginService while keeping a different Output mode.","commonSituations":"Hand-assembling a Definition struct instead of using the framework's builder, adding a plugin service endpoint by copying the output mode line but forgetting PluginServiceOptions(), or changing an existing endpoint's Output mode without removing the stale PluginService field.","solutions":["Assign definition.PluginService = &PluginServiceOptions().PluginService whenever Output is PluginServiceOutput","Or set Output to the correct non-plugin mode if the endpoint is not a plugin service","Use the framework's endpoint builder so Output mode and protocol declaration are set together"],"exampleFix":"// before\ndefinition := apicontract.Definition{Output: apicontract.PluginServiceOutput}\n// after\ndefinition := apicontract.Definition{Output: apicontract.PluginServiceOutput,\n    Data: reflect.TypeFor[apicontract.PluginServiceContent](),\n    PluginService: &apicontract.PluginServiceOptions().PluginService}","handlingStrategy":"validation","validationCode":"if (definition.Output == apicontract.PluginServiceOutput) != (definition.PluginService != nil) {\n    return errors.New(\"plugin service output and protocol declaration must be set together\")\n}","typeGuard":null,"tryCatchPattern":"if err := apicontract.BuildBundle(endpoints); err != nil {\n    if strings.Contains(err.Error(), \"protocol declaration\") { log.Fatalf(\"endpoint definition mismatch: %v\", err) }\n    return err\n}","preventionTips":["Pair Output mode and PluginService declaration in a single constructor function","Never hand-assemble Definition structs; use the framework builders","Run bundle construction in CI so BuildBundle errors surface before deployment"],"tags":["bundle-validation","configuration","go","plugin-service"],"backgroundTag":"missing-required-config-field","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"}