{"record":{"id":"17025a1870d638f6","repo":"GoogleCloudPlatform/microservices-demo","slug":"failed-to-convert-currency-v","errorCode":null,"errorMessage":"failed to convert currency: %+v","messagePattern":"failed to convert currency: %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/checkoutservice/main.go","lineNumber":364,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"failed to get product #%q\", item.GetProductId())\n\t\t}\n\t\tprice, err := cs.convertCurrency(ctx, product.GetPriceUsd(), userCurrency)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to convert price of %q to %s\", item.GetProductId(), userCurrency)\n\t\t}\n\t\tout[i] = &pb.OrderItem{\n\t\t\tItem: item,\n\t\t\tCost: price}\n\t}\n\treturn out, nil\n}\n\nfunc (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money, toCurrency string) (*pb.Money, error) {\n\tresult, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{\n\t\tFrom:   from,\n\t\tToCode: toCurrency})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to convert currency: %+v\", err)\n\t}\n\treturn result, err\n}\n\nfunc (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, paymentInfo *pb.CreditCardInfo) (string, error) {\n\tpaymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{\n\t\tAmount:     amount,\n\t\tCreditCard: paymentInfo})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not charge the card: %+v\", err)\n\t}\n\treturn paymentResp.GetTransactionId(), nil\n}\n\nfunc (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email string, order *pb.OrderResult) error {\n\t_, err := pb.NewEmailServiceClient(cs.emailSvcConn).SendOrderConfirmation(ctx, &pb.SendOrderConfirmationRequest{\n\t\tEmail: email,\n\t\tOrder: order})","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/GoogleCloudPlatform/microservices-demo/blob/72ba613a05f7fcee51cf1d0badff401b6ae7074d/src/checkoutservice/main.go#L346-L382","documentation":"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.","triggerScenarios":"pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(...) errors: toCurrency unsupported, from Money nil, currencyservice unavailable, or timeout (unbounded since context is TODO).","commonSituations":"currencyservice deployment down; unsupported currency code from client; env/config pointing checkout at wrong service address; this wrapper nested inside errors 22 and 27.","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."],"exampleFix":"// before\nresult, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{\n\tFrom:   from,\n\tToCode: toCurrency})\n// after\nctx, cancel := context.WithTimeout(ctx, 3*time.Second)\ndefer cancel()\nresult, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(ctx, &pb.CurrencyConversionRequest{\n\tFrom:   from,\n\tToCode: toCurrency})\nif err != nil {\n\treturn nil, fmt.Errorf(\"failed to convert currency: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"if from == nil || toCurrency == \"\" {\n\treturn status.Error(codes.InvalidArgument, \"money and target currency required\")\n}\nif !isSupportedCurrency(toCurrency) {\n\treturn status.Error(codes.InvalidArgument, \"unsupported target currency\")\n}","typeGuard":"func isConvertible(m *pb.Money, to string) bool {\n\treturn m != nil && m.GetCurrencyCode() != \"\" && to != \"\" && isSupportedCurrency(to)\n}","tryCatchPattern":"result, err := currencyClient.Convert(ctx, req)\nif err != nil {\n\tswitch status.Code(err) {\n\tcase codes.Unavailable:\n\t\treturn retryWithBackoff(ctx, req)\n\tcase codes.InvalidArgument:\n\t\treturn fmt.Errorf(\"bad conversion request: %w\", err)\n\tdefault:\n\t\treturn fmt.Errorf(\"failed to convert currency: %w\", err)\n\t}\n}","preventionTips":["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"],"tags":["grpc","currency","context","microservices"],"backgroundTag":"currency-conversion-failed","analyzedSha":"72ba613a05f7fcee51cf1d0badff401b6ae7074d","analyzedAt":"2026-09-02T01:15:09.673Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}