GoogleCloudPlatform/microservices-demo · error

failed to convert price of %q to %s

Error message

failed to convert price of %q to %s

What it means

After fetching the product, prepOrderItems converts its USD price to the user's currency via convertCurrency; failure is wrapped with the product ID and target currency. The underlying error is again swallowed, hiding whether the cause was an unsupported currency or a currency-service outage.

Source

Thrown at src/checkoutservice/main.go:350

func (cs *checkoutService) emptyUserCart(ctx context.Context, userID string) error {
	if _, err := pb.NewCartServiceClient(cs.cartSvcConn).EmptyCart(ctx, &pb.EmptyCartRequest{UserId: userID}); err != nil {
		return fmt.Errorf("failed to empty user cart during checkout: %+v", err)
	}
	return nil
}

func (cs *checkoutService) prepOrderItems(ctx context.Context, items []*pb.CartItem, userCurrency string) ([]*pb.OrderItem, error) {
	out := make([]*pb.OrderItem, len(items))
	cl := pb.NewProductCatalogServiceClient(cs.productCatalogSvcConn)

	for i, item := range items {
		product, err := cl.GetProduct(ctx, &pb.GetProductRequest{Id: item.GetProductId()})
		if err != nil {
			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
}

View on GitHub (pinned to 72ba613a05)

Solutions

  1. Log the wrapped error with %w to expose the real cause.
  2. Validate userCurrency against currencyservice's supported currency list before checkout.
  3. Check currencyservice pods/logs and connectivity from checkoutservice.
  4. Confirm the product's PriceUsd is populated (not nil/zero) in catalog data.

Example fix

// before
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)
}
// after
price, err := cs.convertCurrency(ctx, product.GetPriceUsd(), userCurrency)
if err != nil {
	return nil, fmt.Errorf("failed to convert price of %q to %s: %w", item.GetProductId(), userCurrency, err)
}
Defensive patterns

Strategy: validation

Validate before calling

if !isSupportedCurrency(userCurrency) {
	return status.Error(codes.InvalidArgument, "unsupported currency: "+userCurrency)
}
if product.GetPriceUsd() == nil {
	return status.Error(codes.FailedPrecondition, "product price missing")
}

Type guard

func isConvertiblePrice(m *pb.Money) bool {
	return m != nil && m.GetUnits() >= 0 && m.GetCurrencyCode() == "USD"
}

Try / catch

price, err := cs.convertCurrency(ctx, product.GetPriceUsd(), userCurrency)
if err != nil {
	if status.Code(err) == codes.InvalidArgument {
		return fmt.Errorf("currency %s not supported: %w", userCurrency, err)
	}
	return fmt.Errorf("failed to convert price to %s: %w", userCurrency, err)
}

Prevention

When it happens

Trigger: cs.convertCurrency(ctx, product.GetPriceUsd(), userCurrency) errors: userCurrency not supported by currencyservice, Money struct empty/zero, or currencyservice unavailable/deadline exceeded.

Common situations: Client sends a currency code (e.g. 'GBP ') with wrong case or whitespace not in the supported list; currencyservice down; currency conversion config/env changed after a deploy.

Related errors


AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02). Data as JSON: /api/errors/415aef4e5e36b59e. Report an issue: GitHub.