siyuan-note/siyuan · error

undeclared plugin admission status

Error message

undeclared plugin admission status

What it means

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.

Solutions

  1. Return only one of the declared admission statuses: 400, 404, 500, or 503
  2. If the desired rejection code differs, use a different variant (e.g. PluginServiceJSON with that status) instead of admission mode
  3. If the contract itself should allow the status, update the AdmissionStatuses declaration in PluginServiceOptions and the validation switch together

Example fix

// before
StreamPluginService(PluginServiceAdmission, 403, serve)
// after
StreamPluginService(PluginServiceAdmission, 400, serve)
Defensive patterns

Strategy: validation

Validate before calling

allowed := []int{400, 404, 500, 503}
if mode == apicontract.PluginServiceAdmission && !slices.Contains(allowed, status) {
	return fmt.Errorf("admission status %d not in %v", status, allowed)
}

Try / catch

defer func() {
	if rec := recover(); rec != nil {
		log.Printf("invalid admission status: %v", rec)
	}
}() // around StreamPluginService(PluginServiceAdmission, ...)

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/2488b4190f738c3e. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/plugin_service_protocol.go:150

func validatePluginServiceStatus(mode PluginServiceMode, status int) error {
	if status < 100 || status > 999 {
		return fmt.Errorf("invalid plugin service HTTP status: %d", status)
	}
	known := false
	for _, variant := range PluginServiceOptions().PluginService.Variants {
		if variant.Mode == mode {
			known = true
			break
		}
	}
	if !known {
		return fmt.Errorf("unknown plugin service mode: %s", mode)
	}
	switch mode {
	case PluginServiceAdmission:
		if status != 400 && status != 404 && status != 500 && status != 503 {
			return fmt.Errorf("undeclared plugin admission status")
		}
	case PluginServiceRedirect:
		if status != 201 && (status < 300 || status > 308) {
			return fmt.Errorf("invalid plugin redirect status")
		}
	case PluginServiceWebSocket:
		if status != 101 && status != 400 && status != 500 {
			return fmt.Errorf("invalid plugin WebSocket status")
		}
	case PluginServiceSSE:
		if status != 200 && status != 500 {
			return fmt.Errorf("invalid plugin SSE status")
		}
	}
	return nil
}

func (b *Bundle) validatePluginServiceHTTPResponse(endpoint EndpointSchema, status int, contentType string, payload []byte) error {

View on GitHub (pinned to 9f775e8a12)