GoogleCloudPlatform/microservices-demo · error
failed to get product #
Error message
failed to get product #%q
What it means
prepOrderItems fails when ProductCatalogService.GetProduct errors for a cart item; the product ID is interpolated with %q. Note the underlying error is swallowed (not wrapped), so only the product ID is visible. Commonly caused by cart entries referencing products that no longer exist in the catalog.
Solutions
- Verify the product ID exists via ProductCatalogService.ListProducts or the catalog data (products.json).
- Check productcatalogservice health and logs.
- Wrap the underlying error (%w) so root cause is visible in logs instead of being discarded.
- Clean up stale carts: validate cart items against the catalog before checkout, or evict unknown IDs.
- Ensure checkoutservice can reach productcatalogservice over gRPC.
Example fix
// before
product, err := cl.GetProduct(ctx, &pb.GetProductRequest{Id: item.GetProductId()})
if err != nil {
return nil, fmt.Errorf("failed to get product #%q", item.GetProductId())
}
// after
product, err := cl.GetProduct(ctx, &pb.GetProductRequest{Id: item.GetProductId()})
if err != nil {
return nil, fmt.Errorf("failed to get product #%q: %w", item.GetProductId(), err)
} Defensive patterns
Strategy: validation
Validate before calling
// validate cart items against the catalog before checkout
for _, item := range cartItems {
if _, err := catalogClient.GetProduct(ctx, &pb.GetProductRequest{Id: item.GetProductId()}); err != nil {
// remove item from cart or reject checkout early
}
} Type guard
func isValidProductID(id string) bool {
return id != "" && productCatalogContains(id) // cached catalog ID set
} Try / catch
product, err := catalogClient.GetProduct(ctx, req)
if err != nil {
if status.Code(err) == codes.NotFound {
return fmt.Errorf("product %q no longer exists: %w", id, err)
}
return fmt.Errorf("failed to get product %q: %w", id, err)
} Prevention
- Always wrap with %w — never discard the underlying error
- Evict deleted product IDs from carts
- Re-seed catalog with stable IDs
- Monitor catalog service availability
When it happens
Trigger: cl.GetProduct(ctx, &pb.GetProductRequest{Id: item.GetProductId()}) returns an error: product ID not found in the catalog, catalog service unavailable, or RPC deadline exceeded.
Common situations: Stale cart data referencing removed/renamed product SKUs; productcatalogservice pod down; catalog data re-seeded with different IDs; typo'd product ID injected by clients.
Related errors
- could not charge the card: %+v
- could not retrieve cart
- could not retrieve currencies
- could not retrieve products
- failed to add to cart
AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02).
Data as JSON: /api/errors/40958b6717fdc918.
Report an issue: GitHub.
Appendix: source
Thrown at src/checkoutservice/main.go:346
}
return cart.GetItems(), nil
}
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)View on GitHub (pinned to 72ba613a05)