GoogleCloudPlatform/microservices-demo · error · ErrInvalidValue
one of the specified money values is invalid
Error message
one of the specified money values is invalid
What it means
ErrInvalidValue is returned by money.Sum when either operand of the addition is not a valid pb.Money: the sign of units and nanos must match and nanos must be within [-999999999, 999999999] (checked by IsValid). The library refuses to do arithmetic on malformed money values rather than produce silently wrong results.
Source
Thrown at src/checkoutservice/money/money.go:30
// See the License for the specific language governing permissions and
// 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 isView on GitHub (pinned to 72ba613a05)
Solutions
- Validate inputs with money.IsValid(l/r) before calling Sum
- Normalize nanos: carry overflow into units so |nanos| < 1e9 and signs agree
- Fix the producer of the Money value (e.g. conversion code) instead of the arithmetic site
- Check the failing value's units/nanos signs in logs to find where it was constructed
Example fix
// before
sum, _ := money.Sum(pb.Money{Units: 1, Nanos: -5}, pb.Money{Units: 0, Nanos: 10})
// after
l := pb.Money{Units: 1, Nanos: -5}
r := pb.Money{Units: 0, Nanos: 10}
if !money.IsValid(l) || !money.IsValid(r) {
return errors.New("invalid money input")
}
sum, err := money.Sum(l, r) Defensive patterns
Strategy: validation
Validate before calling
func safeSum(l, r pb.Money) (pb.Money, error) {
if !money.IsValid(l) || !money.IsValid(r) {
return pb.Money{}, fmt.Errorf("invalid money input: %+v, %+v", l, r)
}
return money.Sum(l, r)
} Type guard
func validMoney(m pb.Money) bool {
return money.IsValid(m)
} Try / catch
sum, err := money.Sum(l, r)
if err != nil {
if errors.Is(err, money.ErrInvalidValue) {
// log and reject the malformed operand
return fmt.Errorf("cannot sum malformed money: %w", err)
}
return err
} Prevention
- Run money.IsValid on any pb.Money received from external systems before arithmetic
- Normalize nanos (carry into units) when building Money from floats/decimals
- Add unit tests covering sign-mismatch and nanos-overflow cases
- Never hand-construct Money values with opposite-signed units/nanos
When it happens
Trigger: Calling Sum with a Money that has units and nanos of opposite signs (e.g. {+1,-1}) or nanos out of range (|nanos| >= 1000000000), as exercised in money_test.go.
Common situations: Constructing pb.Money from raw database or external API fields without normalizing; converting from float/decimal incorrectly producing nanos >= 1e9; manual hand-built values in tests or gRPC clients.
Related errors
- one of the specified money values is invalid
- mismatching currency codes
- mismatching currency codes
- invalid validation error format
- InvalidCreditCard
AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02).
Data as JSON: /api/errors/9598ff43d6a6b001.
Report an issue: GitHub.