{"record":{"id":"5e0996ecb3a4286c","repo":"temporalio/temporal","slug":"expect-at-least-one-reservation","errorCode":null,"errorMessage":"expect at least one reservation","messagePattern":"expect at least one reservation","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/quotas/multi_reservation_impl.go","lineNumber":21,"sourceCode":"import (\n\t\"time\"\n)\n\ntype (\n\tMultiReservationImpl struct {\n\t\tok           bool\n\t\treservations []Reservation\n\t}\n)\n\nvar _ Reservation = (*MultiReservationImpl)(nil)\n\nfunc NewMultiReservation(\n\tok bool,\n\treservations []Reservation,\n) *MultiReservationImpl {\n\tif ok && len(reservations) == 0 {\n\t\tpanic(\"expect at least one reservation\")\n\t}\n\treturn &MultiReservationImpl{\n\t\tok:           ok,\n\t\treservations: reservations,\n\t}\n}\n\n// OK returns whether the limiter can provide the requested number of tokens\nfunc (r *MultiReservationImpl) OK() bool {\n\treturn r.ok\n}\n\n// Cancel indicates that the reservation holder will not perform the reserved action\n// and reverses the effects of this Reservation on the rate limit as much as possible\nfunc (r *MultiReservationImpl) Cancel() {\n\tr.CancelAt(time.Now())\n}\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/quotas/multi_reservation_impl.go#L3-L39","documentation":"NewMultiReservation wraps the results of reserving from multiple rate limiters. It panics when ok==true but the reservations slice is empty — an inconsistent state, since a successful multi-reservation must contain at least one underlying reservation.","triggerScenarios":"Calling NewMultiReservation(true, nil) or NewMultiReservation(true, []quotas.Reservation{}) — e.g. when Reserve loops over limiters but collects no reservations while still reporting ok.","commonSituations":"Buggy custom RateLimiter whose Reserve returns ok=true with a nil reservation; assembling reservations from a filtered list that dropped all entries.","solutions":["Only pass ok=true when at least one reservation was collected (len(reservations) > 0)","Fix the underlying rate limiter that returns a successful reservation without a Reservation object","If no limiters participated, pass ok=false instead"],"exampleFix":"// before\nres := quotas.NewMultiReservation(ok, collected)\n// after\nok = ok && len(collected) > 0\nres := quotas.NewMultiReservation(ok, collected)","handlingStrategy":"validation","validationCode":"ok = ok && len(reservations) > 0\nres := quotas.NewMultiReservation(ok, reservations)","typeGuard":null,"tryCatchPattern":"func() (r quotas.Reservation) {\n    defer func() {\n        if recover() != nil { r = failedReservation }\n    }()\n    return quotas.NewMultiReservation(ok, reservations)\n}()","preventionTips":["Only set ok=true when at least one reservation was collected","Test custom RateLimiters return non-nil Reservation on success","Treat an empty reservation list as a failed reserve"],"tags":["go","panic","rate-limiting","invariant-violation"],"backgroundTag":"inconsistent-reservation-state","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}