GoogleCloudPlatform/microservices-demo · error
failed to convert currency: %+v
Error message
failed to convert currency: %+v
What it means
convertCurrency is the inner wrapper for CurrencyService.Convert failures; both shipping-cost and product-price conversions funnel through it. Notably it uses context.TODO() instead of the caller's ctx, so cancellations and deadlines do not propagate, and the call can outlive the request. Errors here usually mean currencyservice is unreachable or rejected the currency code.
Source
Thrown at src/checkoutservice/main.go:364
return nil, fmt.Errorf("failed to get product #%q", item.GetProductId())
}
price, err := cs.convertCurrency(ctx, product.GetPriceUsd(), userCurrency)
if err != nil {
return nil, fmt.Errorf("failed to convert price of %q to %s", item.GetProductId(), userCurrency)
}
out[i] = &pb.OrderItem{
Item: item,
Cost: price}
}
return out, nil
}
func (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money, toCurrency string) (*pb.Money, error) {
result, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{
From: from,
ToCode: toCurrency})
if err != nil {
return nil, fmt.Errorf("failed to convert currency: %+v", err)
}
return result, err
}
func (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, paymentInfo *pb.CreditCardInfo) (string, error) {
paymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{
Amount: amount,
CreditCard: paymentInfo})
if err != nil {
return "", fmt.Errorf("could not charge the card: %+v", err)
}
return paymentResp.GetTransactionId(), nil
}
func (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email string, order *pb.OrderResult) error {
_, err := pb.NewEmailServiceClient(cs.emailSvcConn).SendOrderConfirmation(ctx, &pb.SendOrderConfirmationRequest{
Email: email,
Order: order})View on GitHub (pinned to 72ba613a05)
Solutions
- Replace context.TODO() with the passed ctx so deadlines/cancellation propagate.
- Check the gRPC status code: Unavailable -> check currencyservice health; InvalidArgument -> check currency code and Money payload.
- Validate toCurrency against supported currencies before calling Convert.
- Verify cs.currencySvcConn dial target and network reachability.
- Add a timeout to the context to avoid hung conversions.
Example fix
// before
result, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{
From: from,
ToCode: toCurrency})
// after
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
result, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(ctx, &pb.CurrencyConversionRequest{
From: from,
ToCode: toCurrency})
if err != nil {
return nil, fmt.Errorf("failed to convert currency: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if from == nil || toCurrency == "" {
return status.Error(codes.InvalidArgument, "money and target currency required")
}
if !isSupportedCurrency(toCurrency) {
return status.Error(codes.InvalidArgument, "unsupported target currency")
} Type guard
func isConvertible(m *pb.Money, to string) bool {
return m != nil && m.GetCurrencyCode() != "" && to != "" && isSupportedCurrency(to)
} Try / catch
result, err := currencyClient.Convert(ctx, req)
if err != nil {
switch status.Code(err) {
case codes.Unavailable:
return retryWithBackoff(ctx, req)
case codes.InvalidArgument:
return fmt.Errorf("bad conversion request: %w", err)
default:
return fmt.Errorf("failed to convert currency: %w", err)
}
} Prevention
- Use the caller's context, never context.TODO()
- Add a per-call timeout to conversion RPCs
- Cache supported-currency list and validate up front
- Alert on currencyservice Unavailable rates
When it happens
Trigger: pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(...) errors: toCurrency unsupported, from Money nil, currencyservice unavailable, or timeout (unbounded since context is TODO).
Common situations: currencyservice deployment down; unsupported currency code from client; env/config pointing checkout at wrong service address; this wrapper nested inside errors 22 and 27.
Related errors
- failed to convert shipping cost to currency: %+v
- failed to convert price of %q to %s
- could not retrieve currencies
- failed to do currency conversion for product %s
- failed to convert currency
AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02).
Data as JSON: /api/errors/17025a1870d638f6.
Report an issue: GitHub.