Quick answer: HasTag matches the tag and its children; HasTagExact matches only the exact tag. A query checking for “State.Stunned” won’t fire on “State.Stunned.Hard” with the exact variant.
An ability is gated by a GameplayTagQuery for State.Debuff. The character has State.Debuff.Poison — but the query returns false.
Hierarchical Tags
GameplayTags are hierarchical: State.Debuff.Poison is a child of State.Debuff. Whether a query matches a child depends on which check it uses.
HasTag vs HasTagExact
Container.HasTag(State.Debuff)— true if the container hasState.Debuffor any child (Poison, Burn…).Container.HasTagExact(State.Debuff)— true only if the container has literallyState.Debuff.
The same split exists for HasAny / HasAnyExact and HasAll / HasAllExact.
Tag Queries
FGameplayTagQuery built in the editor uses the non-exact match by default for “Match Tag”. If you authored it expecting exact behavior (or vice versa), it won’t do what you think — re-check each clause.
Register the Tags
Also confirm both tags actually exist in the Gameplay Tag table. A query against a tag that isn’t registered silently never matches.
Verifying
Apply State.Debuff.Poison; the “has any debuff” query fires. A query specifically for the exact parent tag doesn’t — as intended. Both behaviors are now deliberate.
“HasTag matches children; HasTagExact doesn’t. Choose per check — the hierarchy is the whole point of tags.”
Design your tag hierarchy so the non-exact match is usually what you want — that’s the idiomatic GAS pattern.