siyuan-note/siyuan · error

invalid plugin SSE status

Error message

invalid plugin SSE status

What it means

SSE-mode plugin service responses may only use status 200 (stream started) or 500 (stream failed to start); validatePluginServiceStatus rejects anything else. This guarantees the event-stream contract only ever advertises a successful stream or an explicit server failure.

Solutions

  1. Return 200 when the event stream starts, 500 when it cannot
  2. Handle auth/rejection logic before entering the SSE variant, or use 500 for unrecoverable stream-start failures
  3. Use a different variant (e.g. admission or JSON) for gating responses

Example fix

// before
StreamPluginService(PluginServiceSSE, http.StatusNoContent, serve)
// after
StreamPluginService(PluginServiceSSE, http.StatusOK, serve)
Defensive patterns

Strategy: validation

Validate before calling

func validSSEStatus(status int) bool {
	return status == http.StatusOK || status == http.StatusInternalServerError
}

Try / catch

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

Prevention

When it happens

Trigger: Calling StreamPluginService(PluginServiceSSE, status, serve) or ValidatePluginServiceResponse with sse mode and a status other than 200 or 500 (e.g. 204, 404, or 403).

Common situations: Trying to signal 'no events' with 204; using 401/403 to gate an SSE stream; copying a redirect status into an SSE response.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/plugin_service_protocol.go:162

	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 {
	if status < 100 || status > 999 {
		return fmt.Errorf("invalid plugin service HTTP status")
	}
	if endpoint.Method == "HEAD" || status < 200 || status == 204 || status == 304 {
		if len(payload) > 0 {
			return fmt.Errorf("plugin service response forbids a body")
		}
		return nil
	}
	// 原始文件、代理及插件自选媒体允许任意字节,具体分支由 ValidatePluginServiceResponse 校验。
	return nil
}

View on GitHub (pinned to 9f775e8a12)