{"record":{"id":"269346dbd6a30dbc","repo":"ppy/osu","slug":"hitobjectcontainer-should-be-set-before-checkhitta","errorCode":null,"errorMessage":"HitObjectContainer should be set before CheckHittable is called.","messagePattern":"HitObjectContainer should be set before CheckHittable is called\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game.Rulesets.Osu/UI/LegacyHitPolicy.cs","lineNumber":39,"sourceCode":"    public class LegacyHitPolicy : IHitPolicy\r\n    {\r\n        public IHitObjectContainer? HitObjectContainer { get; set; }\r\n\r\n        private readonly double hittableRange;\r\n\r\n        public LegacyHitPolicy(double hittableRange = OsuHitWindows.MISS_WINDOW)\r\n        {\r\n            this.hittableRange = hittableRange;\r\n        }\r\n\r\n        public void HandleHit(DrawableHitObject hitObject)\r\n        {\r\n        }\r\n\r\n        public virtual ClickAction CheckHittable(DrawableHitObject hitObject, double time, HitResult result)\r\n        {\r\n            if (HitObjectContainer == null)\r\n                throw new InvalidOperationException($\"{nameof(HitObjectContainer)} should be set before {nameof(CheckHittable)} is called.\");\r\n\r\n            var aliveObjects = HitObjectContainer.AliveObjects.ToList();\r\n            int index = aliveObjects.IndexOf(hitObject);\r\n\r\n            if (index > 0)\r\n            {\r\n                var previousHitObject = (DrawableOsuHitObject)aliveObjects[index - 1];\r\n                if (previousHitObject.HitObject.StackHeight > 0 && !previousHitObject.AllJudged)\r\n                    return ClickAction.Ignore;\r\n            }\r\n\r\n            if (result == HitResult.None)\r\n                return ClickAction.Shake;\r\n\r\n            foreach (DrawableHitObject testObject in aliveObjects)\r\n            {\r\n                if (testObject.AllJudged)\r\n                    continue;\r","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game.Rulesets.Osu/UI/LegacyHitPolicy.cs#L21-L57","documentation":"LegacyHitPolicy.CheckHittable() requires HitObjectContainer to enumerate alive objects and determine click ordering for stacked hit objects. The policy must be initialized with HitObjectContainer before any gameplay hit-checking occurs. This is a setup invariant enforced at call time.","triggerScenarios":"Calling CheckHittable(hitObject, time, result) before setting policy.HitObjectContainer = container. The null check throws immediately.","commonSituations":"Custom ruleset integration that creates a LegacyHitPolicy but forgets to wire HitObjectContainer during playfield load; test setup that constructs a policy without initializing the container; lifecycle ordering where the policy is queried before the playfield finishes loading.","solutions":["Set HitObjectContainer on the policy before gameplay starts: policy.HitObjectContainer = HitObjectContainer;.","Ensure the policy is assigned and initialized during the Playfield's LoadComplete or load step, before any hit objects are processed."],"exampleFix":"// before\nvar policy = new LegacyHitPolicy();\npolicy.CheckHittable(hitObject, time, result); // HitObjectContainer is null\n\n// after\nvar policy = new LegacyHitPolicy();\npolicy.HitObjectContainer = HitObjectContainer;\npolicy.CheckHittable(hitObject, time, result);","handlingStrategy":"validation","validationCode":"// Ensure HitObjectContainer is set before gameplay\nif (policy.HitObjectContainer == null)\n    throw new InvalidOperationException(\"HitObjectContainer must be set before gameplay.\");\npolicy.HitObjectContainer = HitObjectContainer;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize all IHitPolicy instances with HitObjectContainer during Playfield.LoadComplete, before any hit objects become interactive.","Add a unit test that verifies CheckHittable throws cleanly when the container is set (integration test).","Consider making HitObjectContainer a constructor parameter instead of a settable property to prevent this class of error."],"tags":["osu-ruleset","hit-policy","initialization","gameplay"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}