{"record":{"id":"d4cf4c700a936d4c","repo":"ppy/osu","slug":"hitobjectcontainer-should-be-set-before-checkhitta-d4cf4c","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/StartTimeOrderedHitPolicy.cs","lineNumber":29,"sourceCode":"\r\nnamespace osu.Game.Rulesets.Osu.UI\r\n{\r\n    /// <summary>\r\n    /// Ensures that <see cref=\"HitObject\"/>s are hit in-order of their start times. Affectionately known as \"note lock\".\r\n    /// If a <see cref=\"HitObject\"/> is hit out of order:\r\n    /// <list type=\"number\">\r\n    /// <item><description>The hit is blocked if it occurred earlier than the previous <see cref=\"HitObject\"/>'s start time.</description></item>\r\n    /// <item><description>The hit causes all previous <see cref=\"HitObject\"/>s to missed otherwise.</description></item>\r\n    /// </list>\r\n    /// </summary>\r\n    public class StartTimeOrderedHitPolicy : IHitPolicy\r\n    {\r\n        public IHitObjectContainer? HitObjectContainer { get; set; }\r\n\r\n        public 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            DrawableHitObject? blockingObject = null;\r\n\r\n            foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime))\r\n            {\r\n                if (hitObjectCanBlockFutureHits(obj))\r\n                    blockingObject = obj;\r\n            }\r\n\r\n            if (blockingObject != null)\r\n            {\r\n                // A hit is disallowed if:\r\n                // 1. The last blocking hitobject has not yet been judged.\r\n                // 2. The current time is before the last hitobject's start time.\r\n                //\r\n                // Hits at exactly the same time as the blocking hitobject are allowed for maps that contain simultaneous hitobjects (e.g. /b/372245).\r\n                if (!blockingObject.Judged && time < blockingObject.HitObject.StartTime)\r\n                    return ClickAction.Shake;\r","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game.Rulesets.Osu/UI/StartTimeOrderedHitPolicy.cs#L11-L47","documentation":"StartTimeOrderedHitPolicy.CheckHittable() requires HitObjectContainer to enumerate hit objects up to the target's start time and determine if a blocking object precedes it. The policy must be initialized with HitObjectContainer before hit-checking. This mirrors the LegacyHitPolicy invariant but in a different policy implementation.","triggerScenarios":"Calling CheckHittable before setting HitObjectContainer on the StartTimeOrderedHitPolicy instance.","commonSituations":"Switching from LegacyHitPolicy to StartTimeOrderedHitPolicy without ensuring the same initialization is performed; test setup omission; ruleset configuration that creates a policy lazily but queries it eagerly.","solutions":["Set HitObjectContainer on the policy before gameplay starts.","Ensure the policy initialization happens in the playfield's load step, consistently for all policy implementations."],"exampleFix":"// before\nvar policy = new StartTimeOrderedHitPolicy();\npolicy.CheckHittable(hitObject, time, result); // HitObjectContainer is null\n\n// after\nvar policy = new StartTimeOrderedHitPolicy();\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.","Apply the same initialization pattern across all policy implementations to ensure consistency.","Consider making HitObjectContainer a constructor parameter to enforce initialization at creation time."],"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"}