SoatDev IT Consulting
SoatDev IT Consulting
  • About us
  • Expertise
  • Services
  • How it works
  • Contact Us
  • News
  • October 20, 2023
  • Rss Fetcher
Photo by Tamara Menzi on Unsplash

The official gRPC documentation hardly mentions error handling. It recommends using Google’s status package if you need a richer error model, but are there any alternatives?

This article came about when I started wondering how error handling works. For example, what’s received by the client if the server returns a standard library error value such as errors.New(“foo”)? And what happens if they have a custom error model such as:

type Misdirection struct {
Culprit string
Scapegoat string
}

func (m *Misdirection) Error() string {
return m.Scapegoat
}

Does it make sense for the server to define sentinel errors the client can use to handle different error cases?

gRPC Is a Framework

A framework is a piece of code that calls code written by users of the framework.

Google has written a gRPC server framework that does all the heavy lifting. When a client sends a request, it finds the correct server method and calls it. An example of a server method is this. What does the framework code look like where the server method is invoked?

The gRPC protocol supports four types of communication between a server and a client: unary RPC calls (synchronous client — server request/response), server streaming RPCs, client streaming RPCs, and bidirectional streaming.

There must be multiple call sites in the framework to the server methods depending on the type of communication, but to make this as simple as possible, I looked into the code for unary RPCs. The following answer is based on version 1.58.2 of Google’s gRPC framework package. On line 1346, the gRPC server framework calls the server method.

An excerpt is shown below:

reply, appErr := md.Handler(info.serviceImpl, ctx, df, s.opts.unaryInt)
if appErr != nil {
...
}

The reply and appErr variables are the response and error values returned by the server method. The type of appErr is the standard library’s error interface. Note this is not what the client receives from the server but rather what the gRPC framework receives before it sends it over the wire.

So, what does the framework do with it? If appErr is non-nil, the framework has an if branch for handling it. How it handles it depends on what the server method returns.

You’ll learn more about this in the next section.

What Is Sent Over the Wire?

First, the framework attempts to convert appErr into a *status.Status value. This conversion fails if the server method returns anything but a *status.Error, such as when returning the standard library’s errors.New(…) or a custom error like my Misdirection example above.

The code comment in the framework code below explains how it handles these non-*status.Error errors, but to further elaborate, status.FromContextError initialises a *status.Status value with the code Unknown. Its message field is assigned the string value appErr.Error(). This is the answer to the question of what the client receives.

appStatus, ok := status.FromError(appErr)
if !ok {
// Convert non-status application error to a status error with code
// Unknown, but handle context errors specifically.
appStatus = status.FromContextError(appErr)
appErr = appStatus.Err()
}

In the case where the conversion ofappErr succeeds, appStatus holds the *status.Status value the server method returned. Either way, appStatus is sent over the wire to the client in the following code snippet.

if e := t.WriteStatus(stream, appStatus); e != nil {
channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
}

Sentinel Errors

Before diving into sentinel errors, I’d say the gRPC error model is designed in such a way as to remove many of the use cases for sentinel errors. For example, when calling an “update resource” operation, it might fail because the subject of the update might not exist in the system.

In such a case, the server method can return a status.Errorf(codes.NotFound, “failed to update resource by id = %s”, id). For the client, it’s probably enough to check the received status code (NotFound) to determine the cause of the failed operation since it knows which resource it requested to update.

The alternative would be to have a sentinel error for the specific error. One such error is ErrPersonNotFound, but that may lead to many similar but different sentinel errors, including ErrDogNotFound, ErrOrderNotFound, etc.

If you need specific errors the client can use to handle a received error correctly, then sentinel errors add value. There’s a catch, though. To make it easy for clients to compare their received errors to your sentinel errors, they must be constructed using the status.Errorf function.

One example is var ErrPersonNotFound = status.Errorf(codes.NotFound, “failed to …”).

But, why is this important?

It’s important because status.Errorf returns a *status.Error value, not a *status.Status. The former is comparable using errors.Is(err, ErrPersonNotFound); the latter is not. This is because *status.Error implements the Is(err target) bool method which is used under the hood when calling errors.Is.

The type *status.Status does not implement this method.

Conclusion

In cases where server methods return non-nil errors, the gRPC server framework in Go always sends a value of type *status.Error over the wire.

Sentinel errors can be used in gRPC implementations as well, but make sure to construct them using the status.Errorf function.


What You Need To Know About gRPC Error Handling was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.

Previous Post
Next Post

Recent Posts

  • Winning capital for your AI startup? Kleida Martiro is leading the conversation at TechCrunch All Stage
  • Nothing releases its first over-the-ear headphones, the $299 Headphone (1)
  • The electric Hummer is almost outselling the F-150 Lightning
  • Nothing releases their first over-the-ear headphones
  • Nothing launches its most expensive flagship yet, Phone (3)

Categories

  • Industry News
  • Programming
  • RSS Fetched Articles
  • Uncategorized

Archives

  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023

Tap into the power of Microservices, MVC Architecture, Cloud, Containers, UML, and Scrum methodologies to bolster your project planning, execution, and application development processes.

Solutions

  • IT Consultation
  • Agile Transformation
  • Software Development
  • DevOps & CI/CD

Regions Covered

  • Montreal
  • New York
  • Paris
  • Mauritius
  • Abidjan
  • Dakar

Subscribe to Newsletter

Join our monthly newsletter subscribers to get the latest news and insights.

© Copyright 2023. All Rights Reserved by Soatdev IT Consulting Inc.