@@ -149,6 +149,8 @@ func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
149149
150150// Details returns a slice of details messages attached to the status.
151151// If a detail cannot be decoded, the error is returned in place of the detail.
152+ // If the detail can be decoded, the proto message returned is of the same
153+ // type that was given to WithDetails().
152154func (s * Status ) Details () []any {
153155 if s == nil || s .s == nil {
154156 return nil
@@ -160,7 +162,38 @@ func (s *Status) Details() []any {
160162 details = append (details , err )
161163 continue
162164 }
163- details = append (details , detail )
165+ // The call to MessageV1Of is required to unwrap the proto message if
166+ // it implemented only the MessageV1 API. The proto message would have
167+ // been wrapped in a V2 wrapper in Status.WithDetails. V2 messages are
168+ // added to a global registry used by any.UnmarshalNew().
169+ // MessageV1Of has the following behaviour:
170+ // 1. If the given message is a wrapped MessageV1, it returns the
171+ // unwrapped value.
172+ // 2. If the given message already implements MessageV1, it returns it
173+ // as is.
174+ // 3. Else, it wraps the MessageV2 in a MessageV1 wrapper.
175+ //
176+ // Since the Status.WithDetails() API only accepts MessageV1, calling
177+ // MessageV1Of ensures we return the same type that was given to
178+ // WithDetails:
179+ // * If the give type implemented only MessageV1, the unwrapping from
180+ // point 1 above will restore the type.
181+ // * If the given type implemented both MessageV1 and MessageV2, point 2
182+ // above will ensure no wrapping is performed.
183+ // * If the given type implemented only MessageV2 and was wrapped using
184+ // MessageV1Of before passing to WithDetails(), it would be unwrapped
185+ // in WithDetails by calling MessageV2Of(). Point 3 above will ensure
186+ // that the type is wrapped in a MessageV1 wrapper again before
187+ // returning. Note that protoc-gen-go doesn't generate code which
188+ // implements ONLY MessageV2 at the time of writing.
189+ //
190+ // NOTE: Status details can also be added using the FromProto method.
191+ // This could theoretically allow passing a Detail message that only
192+ // implements the V2 API. In such a case the message will be wrapped in
193+ // a MessageV1 wrapper when fetched using Details().
194+ // Since protoc-gen-go generates only code that implements both V1 and
195+ // V2 APIs for backward compatibility, this is not a concern.
196+ details = append (details , protoadapt .MessageV1Of (detail ))
164197 }
165198 return details
166199}
0 commit comments