From 07b1da41d578d4479fdf3d117a43ed8a84bef03b Mon Sep 17 00:00:00 2001 From: Saivedant Hava Date: Tue, 12 May 2026 21:10:22 -0400 Subject: [PATCH] fix(serve-grpc): clearer error when given a new-SDK service `bentoml serve-grpc` accepts any importable target. When users point it at a service defined with the `@bentoml.service` decorator (the bentoml>=1.2 programming model) it fails with: type doesn't support gRPC serving That message is correct but unhelpful. gRPC is implemented only for the legacy `bentoml.Service` API, and the new SDK has no gRPC code path, so this case will not work and there is nothing the user can do beyond switching transports or APIs. Detect that situation explicitly in `serve_grpc_production` and raise a message that names the new programming model, points the user at `bentoml serve` for HTTP, and at the legacy `bentoml.Service` API if they need gRPC. Unrelated types still fall through to the existing generic error. Adds a unit test that creates a temporary `@bentoml.service` definition, calls `serve_grpc_production`, and asserts the new wording. Closes #5607 --- src/bentoml/serving.py | 12 +++++ tests/unit/test_serve_grpc_new_sdk_error.py | 50 +++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/unit/test_serve_grpc_new_sdk_error.py diff --git a/src/bentoml/serving.py b/src/bentoml/serving.py index 09c58fd78d1..f5bc84b58ae 100644 --- a/src/bentoml/serving.py +++ b/src/bentoml/serving.py @@ -590,6 +590,18 @@ def serve_grpc_production( svc = load(bento_identifier, working_dir=working_dir) if not isinstance(svc, Service): + try: + from _bentoml_sdk import Service as NewService + except ImportError: + NewService = None # type: ignore[assignment] + if NewService is not None and isinstance(svc, NewService): + raise BentoMLException( + "gRPC serving is not supported for services defined with the " + "`@bentoml.service` decorator (the bentoml>=1.2 programming " + "model). Use `bentoml serve` to run the service over HTTP, or " + "rewrite it using the legacy `bentoml.Service` API if gRPC is " + "required." + ) raise BentoMLException(f"{type(svc)} type doesn't support gRPC serving") from circus.sockets import CircusSocket # type: ignore diff --git a/tests/unit/test_serve_grpc_new_sdk_error.py b/tests/unit/test_serve_grpc_new_sdk_error.py new file mode 100644 index 00000000000..e5422db8959 --- /dev/null +++ b/tests/unit/test_serve_grpc_new_sdk_error.py @@ -0,0 +1,50 @@ +"""Regression test for bentoml#5607. + +`serve-grpc` used to fail with the unhelpful message:: + + type doesn't support gRPC serving + +when pointed at a service defined with the `@bentoml.service` decorator +(the bentoml>=1.2 programming model). gRPC is only implemented for the +legacy `bentoml.Service` API, so this case should raise a clear error +that tells the user what to do instead. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from bentoml.exceptions import BentoMLException +from bentoml.serving import serve_grpc_production + +NEW_SDK_SERVICE = """ +from __future__ import annotations + +import bentoml + + +@bentoml.service(name="grpc_smoke") +class Supervisor: + @bentoml.api + def greet(self, name: str = "world") -> str: + return f"hello {name}" +""" + + +def test_serve_grpc_rejects_new_sdk_service_with_clear_message( + tmp_path: Path, +) -> None: + (tmp_path / "service.py").write_text(NEW_SDK_SERVICE) + + with pytest.raises(BentoMLException) as exc_info: + serve_grpc_production( + "service.py:Supervisor", + working_dir=str(tmp_path), + ) + + message = str(exc_info.value) + assert "gRPC serving is not supported" in message + assert "@bentoml.service" in message + assert "bentoml serve" in message