Skip to content

feat(grpc): preserve unknown detail types when decoding gRPC errors #5905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

fiam
Copy link
Collaborator

@fiam fiam commented Apr 10, 2025

If decoding the status fails, retain the original proto message by
registering it as a detail.

This change also adds a test to verify that unknown types are preserved when:

  • Decoding a gRPC error with unknown detail types.
  • Re-encoding and decoding the error using ToGRPC / FromGRPC.

Additionally, fix an issue where the codes.Code string prefix was
not handled correctly, resulting in a duplicate prefix.

@fiam fiam force-pushed the alberto/grpc-preserve-unknown-types branch 2 times, most recently from c9db49b to 79dccda Compare April 10, 2025 20:50
@@ -45,7 +46,7 @@ func ToGRPC(ctx context.Context, err error) error {

// If the original error was wrapped with more context than the GRPCStatus error,
// copy the original message to the GRPCStatus error
if err.Error() != st.Message() {
if errorHasMoreContext(err, st) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the case where we don't want the original details (or ones set in FromGRPC) to not be added into this error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsGRPCStatus() unwraps until it finds something implementing GRPCStatus(). So if we had something like errors.Wrap(errors.Wrap(st.Err(), "foo") "bar"), it would discard the foo: bar: prefix otherwise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for the opposite, st.Err().Error() prepends the code to st.Message() so naively setting the message again would duplicate the prefix that corresponds to the codes.Code.

Check this playground https://go.dev/play/p/UKFRlAyVlYT

@fiam fiam force-pushed the alberto/grpc-preserve-unknown-types branch from 79dccda to 8630be0 Compare April 10, 2025 21:23
if errMessage := err.Error(); len(errMessage) > len(st.Message()) {
// check if the longer message in errMessage is only due to
// prepending with the status code
prefix := st.Code().String() + ": "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this trying to mimic grpcStatusError.Error(). In that case looks like it is more complicated as some codes have special meaning. Type checking to *grpcStatusError could be better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the check to use *grpcStatusError, let me know if that's what you had in mind. Thanks!

If decoding the status fails, retain the original proto message by
registering it as a detail.

This change also adds a test to verify that unknown types are preserved when:

- Decoding a gRPC error with unknown detail types.
- Re-encoding and decoding the error using ToGRPC / FromGRPC.

Additionally, fix an issue where the codes.Code string prefix was
not handled correctly, resulting in a duplicate prefix.

Signed-off-by: Alberto Garcia Hierro <[email protected]>
@fiam fiam force-pushed the alberto/grpc-preserve-unknown-types branch from 8630be0 to 5848d95 Compare April 15, 2025 12:56
// errorHasMoreContext checks if the original error provides more context by having
// a different message or additional details than the Status.
func errorHasMoreContext(err error, st *status.Status) bool {
if errMessage := err.Error(); len(errMessage) > len(st.Message()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this length check for?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants