siyuan-note/siyuan · error
plugin service output requires a protocol declaration
Error message
plugin service output requires a protocol declaration
What it means
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.
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
Example fix
// before
definition := apicontract.Definition{Output: apicontract.PluginServiceOutput}
// after
definition := apicontract.Definition{Output: apicontract.PluginServiceOutput,
Data: reflect.TypeFor[apicontract.PluginServiceContent](),
PluginService: &apicontract.PluginServiceOptions().PluginService} Defensive patterns
Strategy: validation
Validate before calling
if (definition.Output == apicontract.PluginServiceOutput) != (definition.PluginService != nil) {
return errors.New("plugin service output and protocol declaration must be set together")
} Try / catch
if err := apicontract.BuildBundle(endpoints); err != nil {
if strings.Contains(err.Error(), "protocol declaration") { log.Fatalf("endpoint definition mismatch: %v", err) }
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- invalid plugin service response options
- AI configuration is unavailable
- Conf.Language(249)
- Data synchronization has not been enabled
- empty plugin response contains a body
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7d80f8f51686cd09.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/plugin_service_protocol.go:119
func (e Endpoint[Request, Data]) pluginServiceStatus(response Response[Data]) int {
if e.definition.Output != PluginServiceOutput || e.definition.PluginService == nil {
panic("endpoint does not declare plugin service output")
}
if response.stream == nil {
if response.code == 0 {
panic("plugin service requires a selected response mode")
}
return 200
}
if err := validatePluginServiceStatus(response.pluginServiceMode, response.httpStatus); err != nil {
panic(err)
}
return response.httpStatus
}
func validatePluginServiceDefinition(definition Definition) error {
if (definition.Output == PluginServiceOutput) != (definition.PluginService != nil) {
return fmt.Errorf("plugin service output requires a protocol declaration")
}
if definition.PluginService == nil {
return nil
}
if definition.Data != reflect.TypeFor[PluginServiceContent]() || definition.SSE != nil || definition.Proxy != nil || definition.WebSocket != nil || definition.DataOnError || definition.ErrorStatus != 0 {
return fmt.Errorf("invalid plugin service response options")
}
if !reflect.DeepEqual(definition.PluginService, PluginServiceOptions().PluginService) {
return fmt.Errorf("invalid plugin service protocol variants")
}
return nil
}
func validatePluginServiceStatus(mode PluginServiceMode, status int) error {
if status < 100 || status > 999 {
return fmt.Errorf("invalid plugin service HTTP status: %d", status)
}
known := falseView on GitHub (pinned to 9f775e8a12)