{"record":{"id":"276083859a6b032e","repo":"GoogleCloudPlatform/microservices-demo","slug":"one-of-the-specified-money-values-is-invalid-276083","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/frontend/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/frontend/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/frontend/money/money.go#L12-L48","documentation":"The frontend carries an identical copy of the money helper (src/frontend/money/money.go), whose ErrInvalidValue is declared at money.go:30. It means a pb.Money operand failed IsValid: units/nanos signs mismatch or nanos out of range. Sum refuses arithmetic on malformed money.","triggerScenarios":"Same as error 13: Sum called with units/nanos of opposite signs or |nanos| >= 1000000000, e.g. a Money built from un-normalized external input.","commonSituations":"Duplicated money package drift between frontend and checkoutservice; price values built by hand or converted from floats; protobuf Money produced by another service without validation.","solutions":["Validate with IsValid before summing","Normalize nanos into units so the value satisfies signMatches && validNanos","Fix the producer that emits invalid Money values","Consider deduplicating the money package into a shared module to avoid drift"],"exampleFix":"// before\nout, _ := money.Sum(invalidMoney, other)\n// after\nif !money.IsValid(invalidMoney) {\n    return pb.Money{}, fmt.Errorf(\"invalid money: %+v\", invalidMoney)\n}\nout, err := money.Sum(invalidMoney, other)","handlingStrategy":"validation","validationCode":"if !money.IsValid(price) {\n\treturn errors.New(\"price failed IsValid: units/nanos sign mismatch or nanos out of range\")\n}\nout, err := money.Sum(price, other)","typeGuard":"func isValidMoney(m pb.Money) bool { return money.IsValid(m) }","tryCatchPattern":"out, err := money.Sum(l, r)\nif errors.Is(err, money.ErrInvalidValue) {\n\treturn fmt.Errorf(\"invalid money operand: %+v / %+v\", l, r)\n}","preventionTips":["Normalize nanos when converting from decimal/float sources","Validate Money from other services before arithmetic","Keep the frontend and checkoutservice money packages in sync (shared module)"],"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"}