Files
benilla-octo/crates
gromandClaude Opus 5 fc78e52c25 A NAME = nil line in an addon's saved file deletes the default it just set
grom, 2026-11-05: *"I'd also like to get advanced trade skill window2 working … I can't
even get number 2 to load right now."* His log has three runtime errors from it, all the
same shape:

    AdvancedTradeSkillWindow2.lua:844:  attempt to index global `ATSW_Profession'
    AdvancedTradeSkillWindow2.lua:5931: attempt to index global `ATSW_Bags'
    AdvancedTradeSkillWindow2.lua:5990: attempt to index global `ATSW_Bank'

All three are assigned `{}` at FILE SCOPE — lines 294, 5929 and 5986 — and the chunk ran:
the traceback carries `ATSW_OnEvent` (2504) calling `ATSW_ShowTrainingPoints` (4269), both
past the first of them. So the tables existed and were taken away, and
`benilla-config/saved/AdvancedTradeSkillWindow2.lua` says by what: 19 of its 24 declared
names stood at `NAME = nil`.

An addon's saved file is executed AFTER its own Lua (`0x51f4b5`, "after the files set
their defaults"), which is what makes a nil line destructive rather than merely useless —
in a fresh VM a global is already nil, so the line carries nothing and can only delete.
And once written it is self-sustaining: the nil it forces is what the next shutdown reads
back and writes again, so one bad write is permanent. The reference does not write them,
which is why no real SavedVariables file has ever had a `= nil` line in it.

Both sides, because the write fix alone still costs a session:

  * `saved_variables_bytes_for` skips a nil global. The flat `RegisterForSave` file still
    writes them — it is read BEFORE FrameXML, where a nil line and an absent one mean the
    same thing — so the two paths split and `serialize_saved` takes the flag.
  * `addon::drop_nil_assignments` strips `NAME = nil` lines a previous build already
    wrote, matched narrowly against that addon's own `## SavedVariables` set, one
    assignment per line, logged once per file. A player's hand-written `X = nil` of a name
    the addon never declared is left alone.

It is not one addon: his install carried poisoned files for five — ATSW2 (20 lines),
BigWigs (4), ImprovedErrorFrame (2), MasterTradeSkills (1), SuperCleveRoidMacros (1).

Also the harness gap next door, which is why a capture could not have found this. A
capture answered `GetRealmName()` as "" and `UnitName("player")` as "Benilla" even with
`$WOW_CAPTURE_SAVED=C_Thun-Gromgosh` naming the character whose saved variables it had
just loaded — so `ATSW_Profession[realm][player] = nil` (line 296) raised `table index is
nil` and took 6,000 lines of chunk with it. That is a broken instrument that reads exactly
like the defect being measured, and it hit every addon keyed on `[realm][player]`.
`lifecycle.rs` now seats both from the capture identity, before the addon walk.

Verified: `a_never_assigned_saved_variable_writes_no_line_and_a_nil_line_is_dropped_on_load`
covers both halves. In a real session with his addons, all 24 of ATSW2's variables read as
tables at the first handler and `ATSW_ShowTrainingPoints()` — the function that raised
fifteen times — returns. Addon corpus unchanged at 86/87 clean loads, 86/87 UI probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-11-05 10:37:20 -07:00
..