GoogleCloudPlatform/microservices-demo · error · ErrMismatchingCurrency

mismatching currency codes

Error message

mismatching currency codes

What it means

ErrMismatchingCurrency is returned by money.Sum when the two operands have different CurrencyCode values. Adding money across currencies is meaningless, so the library rejects it explicitly instead of adding raw numbers.

Source

Thrown at src/checkoutservice/money/money.go:31

// limitations under the License.

package money

import (
	"errors"

	pb "github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/genproto"
)

const (
	nanosMin = -999999999
	nanosMax = +999999999
	nanosMod = 1000000000
)

var (
	ErrInvalidValue        = errors.New("one of the specified money values is invalid")
	ErrMismatchingCurrency = errors.New("mismatching currency codes")
)

// IsValid checks if specified value has a valid units/nanos signs and ranges.
func IsValid(m pb.Money) bool {
	return signMatches(m) && validNanos(m.GetNanos())
}

func signMatches(m pb.Money) bool {
	return m.GetNanos() == 0 || m.GetUnits() == 0 || (m.GetNanos() < 0) == (m.GetUnits() < 0)
}

func validNanos(nanos int32) bool { return nanosMin <= nanos && nanos <= nanosMax }

// IsZero returns true if the specified money value is equal to zero.
func IsZero(m pb.Money) bool { return m.GetUnits() == 0 && m.GetNanos() == 0 }

// IsPositive returns true if the specified money value is valid and is
// positive.

View on GitHub (pinned to 72ba613a05)

Solutions

  1. Convert one operand to the other's currency before summing (use currencyconversion service)
  2. Compare l.GetCurrencyCode() == r.GetCurrencyCode() before calling Sum
  3. Ensure the catalog/store returns prices in a consistent currency for the order
  4. Check where the currency code was set (product price vs user currency) for divergence

Example fix

// before
if l.GetCurrencyCode() != r.GetCurrencyCode() {
    return pb.Money{}, ErrMismatchingCurrency
}
// after
if l.GetCurrencyCode() != r.GetCurrencyCode() {
    r, err = convert(r, l.GetCurrencyCode()) // convert via currency service first
    if err != nil { return pb.Money{}, err }
}
Defensive patterns

Strategy: validation

Validate before calling

func safeSum(l, r pb.Money) (pb.Money, error) {
	if l.GetCurrencyCode() != r.GetCurrencyCode() {
		return pb.Money{}, fmt.Errorf("currency mismatch: %s vs %s", l.GetCurrencyCode(), r.GetCurrencyCode())
	}
	return money.Sum(l, r)
}

Type guard

func sameCurrency(l, r pb.Money) bool {
	return l.GetCurrencyCode() == r.GetCurrencyCode()
}

Try / catch

sum, err := money.Sum(l, r)
if err != nil {
	if errors.Is(err, money.ErrMismatchingCurrency) {
		return fmt.Errorf("convert %s to %s before summing", r.GetCurrencyCode(), l.GetCurrencyCode())
	}
	return err
}

Prevention

When it happens

Trigger: Calling Sum (directly or via checkout order-item/product price aggregation) with prices denominated in different currency codes, e.g. USD price plus EUR price.

Common situations: Products priced in different currencies in the catalog; user currency conversion skipped or failed; cart items added under one currency then displayed/ordered under another.

Related errors


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