ppy/osu · error · InvalidOperationException
HitObjectContainer should be set before CheckHittable is cal
Error message
HitObjectContainer should be set before CheckHittable is called.
What it means
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.
Source
Thrown at osu.Game.Rulesets.Osu/UI/LegacyHitPolicy.cs:39
public class LegacyHitPolicy : IHitPolicy
{
public IHitObjectContainer? HitObjectContainer { get; set; }
private readonly double hittableRange;
public LegacyHitPolicy(double hittableRange = OsuHitWindows.MISS_WINDOW)
{
this.hittableRange = hittableRange;
}
public void HandleHit(DrawableHitObject hitObject)
{
}
public virtual ClickAction CheckHittable(DrawableHitObject hitObject, double time, HitResult result)
{
if (HitObjectContainer == null)
throw new InvalidOperationException($"{nameof(HitObjectContainer)} should be set before {nameof(CheckHittable)} is called.");
var aliveObjects = HitObjectContainer.AliveObjects.ToList();
int index = aliveObjects.IndexOf(hitObject);
if (index > 0)
{
var previousHitObject = (DrawableOsuHitObject)aliveObjects[index - 1];
if (previousHitObject.HitObject.StackHeight > 0 && !previousHitObject.AllJudged)
return ClickAction.Ignore;
}
if (result == HitResult.None)
return ClickAction.Shake;
foreach (DrawableHitObject testObject in aliveObjects)
{
if (testObject.AllJudged)
continue;
View on GitHub (pinned to d9c73e12ad)
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.
Example fix
// before var policy = new LegacyHitPolicy(); policy.CheckHittable(hitObject, time, result); // HitObjectContainer is null // after var policy = new LegacyHitPolicy(); policy.HitObjectContainer = HitObjectContainer; policy.CheckHittable(hitObject, time, result);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure HitObjectContainer is set before gameplay
if (policy.HitObjectContainer == null)
throw new InvalidOperationException("HitObjectContainer must be set before gameplay.");
policy.HitObjectContainer = HitObjectContainer; Prevention
- 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.
When it happens
Trigger: Calling CheckHittable(hitObject, time, result) before setting policy.HitObjectContainer = container. The null check throws immediately.
Common situations: 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.
Related errors
- HitObjectContainer should be set before CheckHittable is cal
- HitObjectContainer should be set before HandleHit is called.
- A {hitObject} was hit before it became hittable!
- Can't have zero or fewer stages.
- Unsupported grid type.
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/269346dbd6a30dbc.
Report an issue: GitHub.