Skip to content

Commit d68e44a

Browse files
author
mergetest
committed
fix(auras): guard the AD health mirror against a forbidden bar
Bug #1004: 999x "Frames/Core.lua:127: calling 'SetMinMaxValues' on bad self (Attempt to access forbidden object from code tainted by an AddOn)" reported from a PTR follower dungeon. MirrorHealthValue drives frame.dfADHealthMirror, which is not ours: it is a StatusBar child of the secret aura button, created by AuraContainer's style pass and handed back through the onBar callback for the update loop to feed. Children of that button are access-constrained, so the client can turn the bar forbidden underneath us — a slot reclaimed or re-initialised without the addon observing it leaves the stash pointing at an object we may no longer touch, and every health event then throws. Roster churn in a follower dungeon rebuilds containers constantly, hence the volume. The sibling SetHealthBarValue makes the identical SetMinMaxValues call and never throws, because it drives frame.healthBar, which we own. Probed with the first render call rather than IsForbidden(). The bar lives inside the secret container, so IsForbidden can itself return a SECRET on a healthy bar, and the existing guard idiom (ClickCasting/Frames.lua:1157) treats a secret result as "skip" — that would have disabled the overlay for everyone. Probing the real call cannot false-positive. The stash is deliberately not cleared on failure: Factory owns that reference and nils it on its own teardown paths, and clearing it here would fight the container over a bar that may only be transiently unavailable. The debug warning is one-shot per frame so a persistently forbidden bar does not trade an error spam for a debug spam. Not reproduced locally — the fix is defensive and the root cause is inferred from the stack and the container's ownership model.
1 parent eea8f68 commit d68e44a

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ DandersFrames has been rebuilt for WoW 12.1 (Midnight), which fundamentally chan
2626
* (Aura Designer) Open dropdown menus now close when switching tabs instead of lingering over the new tab.
2727
* (Auras) "Hide Long Debuffs" now also works while "All Debuffs" is enabled. "Keep important debuffs" still requires the category filters.
2828
* (Aura Designer) Fix cooldown swipes on indicators and groups draining in the opposite direction from the buff and debuff bars.
29+
* (Aura Designer) Fix error spam from the health bar overlay, most noticeable in follower dungeons and other content where players join and leave often.
2930
* (Interface) Fix spells with hidden tooltips (like Strength of the Black Ox) showing an empty box in the Aura Filters spell list — they now show their name and spell ID.
3031
* (Aura Designer) One spell picker everywhere — adding indicators, group spells and triggers all use the same spell database picker as Aura Filters, with search, class and category filters, and add-by-ID.
3132
* (Aura Designer) Every indicator has a show/hide eye — turn one off without deleting it.

Frames/Core.lua

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,39 @@ DF.SetHealthBarValue = SetHealthBarValue
124124
local function MirrorHealthValue(bar, unit, frame)
125125
if not bar then return end
126126

127-
bar:SetMinMaxValues(0, 100)
127+
-- The mirror is NOT ours: it is a StatusBar child of the secret aura button
128+
-- (AuraContainer's slot), handed back through the container's onBar callback and
129+
-- stashed on the frame. Children of that button are access-constrained, and the
130+
-- client can turn this one forbidden underneath us — a slot reclaimed or
131+
-- re-initialised without the addon observing it leaves the stash pointing at an
132+
-- object we may no longer touch. Every health event then throws, which is bug
133+
-- #1004: 999x "SetMinMaxValues on bad self (forbidden object)" in a follower
134+
-- dungeon, where roster churn rebuilds containers constantly.
135+
--
136+
-- Probed with the first render call rather than IsForbidden(): this bar lives
137+
-- inside the secret container, so IsForbidden can itself hand back a SECRET on a
138+
-- perfectly healthy bar, and the codebase's guard idiom treats a secret result as
139+
-- "skip" (ClickCasting/Frames.lua:1157). That would silently kill the feature for
140+
-- everyone. Probing the real call cannot false-positive: if it succeeds the bar is
141+
-- drivable, and SetValue below goes to the same object on the same tick.
142+
if not pcall(bar.SetMinMaxValues, bar, 0, 100) then
143+
-- One-shot per frame: a persistently forbidden bar must not trade a 999x
144+
-- error spam for a 999x debug spam. The stash is deliberately NOT cleared —
145+
-- Factory owns that reference and nils it on its own teardown paths, and
146+
-- dropping it here would fight the container over a bar that may just be
147+
-- transiently unavailable.
148+
if frame and not frame.dfMirrorForbiddenLogged then
149+
frame.dfMirrorForbiddenLogged = true
150+
DF:DebugWarn("AURACONTAINER",
151+
"MirrorHealthValue: health mirror bar forbidden, skipping its updates on %s",
152+
tostring(unit))
153+
end
154+
return
155+
end
156+
if frame and frame.dfMirrorForbiddenLogged then
157+
frame.dfMirrorForbiddenLogged = nil -- recovered; allow one log if it happens again
158+
end
159+
128160
local pct = GetSafeHealthPercent(unit)
129161

130162
local db

0 commit comments

Comments
 (0)