Skip to content
Created by

Errors

Similar to the familiar “404 Not Found” and “500 Internal Server Error” status codes in HTTP, Connect uses a set of 16 error codes. These error codes are designed to work consistently across Connect, gRPC, and gRPC-Web protocols.

Connect handlers raise errors using ConnectError:

from connectrpc.code import Code
from connectrpc.errors import ConnectError
from connectrpc.request import RequestContext
async def greet(self, request: GreetRequest, ctx: RequestContext) -> GreetResponse:
if not request.name:
raise ConnectError(Code.INVALID_ARGUMENT, "name is required")
return GreetResponse(greeting=f"Hello, {request.name}!")

Clients catch errors the same way:

from connectrpc.code import Code
from connectrpc.errors import ConnectError
async with GreetServiceClient("http://localhost:8000") as client:
try:
response = await client.greet(GreetRequest(name=""))
except ConnectError as e:
if e.code == Code.INVALID_ARGUMENT:
print(f"Invalid request: {e.message}")
else:
print(f"RPC failed: {e.code} - {e.message}")

Connect uses a set of 16 error codes. The code property of a ConnectError holds one of these codes. All error codes are available through the Code enumeration:

from connectrpc.code import Code
code = Code.INVALID_ARGUMENT
code.value # "invalid_argument"
# Access by name
Code["INVALID_ARGUMENT"] # Code.INVALID_ARGUMENT

The message property contains a descriptive error message. In most cases, the message is provided by the backend implementing the service:

try:
response = await client.greet(GreetRequest(name=""))
except ConnectError as e:
print(e.message) # "name is required"

Errors can include strongly-typed details using Protobuf messages:

from connectrpc.code import Code
from connectrpc.errors import ConnectError
from connectrpc.request import RequestContext
from google.protobuf.struct_pb2 import Struct, Value
async def create_user(self, request: CreateUserRequest, ctx: RequestContext) -> CreateUserResponse:
if not request.email:
error_detail = Struct(fields={
"field": Value(string_value="email"),
"issue": Value(string_value="Email is required")
})
raise ConnectError(
Code.INVALID_ARGUMENT,
"Invalid user request",
details=[error_detail]
)
# ... rest of implementation

Error details are google.protobuf.Any messages that can be unpacked to their original types:

try:
response = await client.some_method(request)
except ConnectError as e:
for detail in e.details:
# Check the type before unpacking
if detail.Is(Struct.DESCRIPTOR):
unpacked = Struct()
detail.Unpack(unpacked)
print(f"Error detail: {unpacked}")

With googleapis-common-protos installed, you can use standard types like:

  • BadRequest: Field violations in a request
  • RetryInfo: When to retry
  • Help: Links to documentation
  • QuotaFailure: Quota violations
  • ErrorInfo: Structured error metadata

Example:

from google.rpc.error_details_pb2 import BadRequest
bad_request = BadRequest()
violation = bad_request.field_violations.add()
violation.field = "email"
violation.description = "Must be a valid email address"
raise ConnectError(
Code.INVALID_ARGUMENT,
"Invalid email format",
details=[bad_request]
)

In the Connect protocol, errors are always JSON:

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"code": "invalid_argument",
"message": "name is required",
"details": [
{
"type": "google.protobuf.Struct",
"value": "base64-encoded-protobuf",
"debug": {"field": "email", "issue": "Email is required"}
}
]
}

See the Connect protocol for a full description of the error detail fields, including type, value, and debug.