{"record":{"id":"9598ff43d6a6b001","repo":"GoogleCloudPlatform/microservices-demo","slug":"one-of-the-specified-money-values-is-invalid","errorCode":null,"errorMessage":"one of the specified money values is invalid","messagePattern":"one of the specified money values is invalid","errorType":"exception","errorClass":"ErrInvalidValue","httpStatus":null,"severity":"error","filePath":"src/checkoutservice/money/money.go","lineNumber":30,"sourceCode":"// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage money\n\nimport (\n\t\"errors\"\n\n\tpb \"github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/genproto\"\n)\n\nconst (\n\tnanosMin = -999999999\n\tnanosMax = +999999999\n\tnanosMod = 1000000000\n)\n\nvar (\n\tErrInvalidValue        = errors.New(\"one of the specified money values is invalid\")\n\tErrMismatchingCurrency = errors.New(\"mismatching currency codes\")\n)\n\n// IsValid checks if specified value has a valid units/nanos signs and ranges.\nfunc IsValid(m pb.Money) bool {\n\treturn signMatches(m) && validNanos(m.GetNanos())\n}\n\nfunc signMatches(m pb.Money) bool {\n\treturn m.GetNanos() == 0 || m.GetUnits() == 0 || (m.GetNanos() < 0) == (m.GetUnits() < 0)\n}\n\nfunc validNanos(nanos int32) bool { return nanosMin <= nanos && nanos <= nanosMax }\n\n// IsZero returns true if the specified money value is equal to zero.\nfunc IsZero(m pb.Money) bool { return m.GetUnits() == 0 && m.GetNanos() == 0 }\n\n// IsPositive returns true if the specified money value is valid and is","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/GoogleCloudPlatform/microservices-demo/blob/72ba613a05f7fcee51cf1d0badff401b6ae7074d/src/checkoutservice/money/money.go#L12-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nsum, _ := money.Sum(pb.Money{Units: 1, Nanos: -5}, pb.Money{Units: 0, Nanos: 10})\n// after\nl := pb.Money{Units: 1, Nanos: -5}\nr := pb.Money{Units: 0, Nanos: 10}\nif !money.IsValid(l) || !money.IsValid(r) {\n    return errors.New(\"invalid money input\")\n}\nsum, err := money.Sum(l, r)","handlingStrategy":"validation","validationCode":"func safeSum(l, r pb.Money) (pb.Money, error) {\n\tif !money.IsValid(l) || !money.IsValid(r) {\n\t\treturn pb.Money{}, fmt.Errorf(\"invalid money input: %+v, %+v\", l, r)\n\t}\n\treturn money.Sum(l, r)\n}","typeGuard":"func validMoney(m pb.Money) bool {\n\treturn money.IsValid(m)\n}","tryCatchPattern":"sum, err := money.Sum(l, r)\nif err != nil {\n\tif errors.Is(err, money.ErrInvalidValue) {\n\t\t// log and reject the malformed operand\n\t\treturn fmt.Errorf(\"cannot sum malformed money: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["go","money","validation","proto"],"backgroundTag":"invalid-money-value","analyzedSha":"72ba613a05f7fcee51cf1d0badff401b6ae7074d","analyzedAt":"2026-09-02T01:15:09.673Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}