Fix null stat_panel read in subsystem fire (#23066)

* Please describe the intent of your changes in a clear fashion.
This PR addresses the `Cannot read null.stat_panel` error occurring in
`SSstatpanels/fire()`.

**Root Cause:**
The primary cause was a race condition during client login. A `client`
object was added to `GLOB.clients` in `client/Login()` before its
`stat_panel` member had been fully initialized. This allowed the
`SSstatpanels/fire()` subsystem, which iterates over `GLOB.clients`, to
attempt to access `target.stat_panel.is_ready()` on a `null`
`stat_panel`, leading to a runtime error. Recent changes increasing the
frequency of `stat_panel` updates exacerbated this issue.

**Solution:**
1. **Reordered Client Initialization:** In
`code/modules/client/client_procs.dm`, the line `GLOB.clients += src`
has been moved to occur *after* `stat_panel = new(src, "statbrowser")`
and `stat_panel.subscribe(...)`. This ensures that a client is only
added to the global list once its `stat_panel` is properly instantiated,
eliminating the race condition.
2. **Defensive Null Checks:** Additional null checks for `stat_panel`
have been added in `code/controllers/subsystems/statpanel.dm`:
* In `fire()`, the condition `!target.stat_panel.is_ready()` was updated
to `!target.stat_panel || !target.stat_panel.is_ready()`.
* In `refresh_client_obj_view()`, an early return
`if(!refresh.stat_panel) return` was added.

These defensive checks provide robustness against any future reordering
issues or unexpected scenarios where `stat_panel` might be null.
* Please make sure that, in the case of mapping changes, you include
images of these changes in the PR's description.
* Please make sure to mark your PR as wip or review required by making a
comment with !wip or !review required
* If you include sprites/sounds/... (assets) that you have not created
yourself specify the license and original author below.
* Ensure that you also credit them in the appropriate location /
changelog as specified in the contributor guidelines

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/example.dmi | ExamplePerson (Example Station) | CC0 |

Fixes
[SERVER-PROD-1M4](https://aurorastation.sentry.io/issues/7547575486/?seerDrawer=true)

---------

Co-authored-by: VMSolidus <evilexecutive@gmail.com>
This commit is contained in:
sentry[bot]
2026-08-20 15:46:08 +00:00
committed by GitHub
co-authored by VMSolidus
parent 933ae32a1a
commit 7565a99703
3 changed files with 8 additions and 2 deletions
+3 -1
View File
@@ -59,7 +59,7 @@ SUBSYSTEM_DEF(statpanels)
var/client/target = currentrun[length(currentrun)]
currentrun.len--
if(!target.stat_panel.is_ready())
if(!target?.stat_panel?.is_ready())
continue
if(target.stat_tab == "Status" && num_fires % status_wait == 0)
@@ -217,6 +217,8 @@ SUBSYSTEM_DEF(statpanels)
refresh_client_obj_view(target)
/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh)
if(!refresh.stat_panel)
return
var/list/turf_items = return_object_images(refresh)
if(!length(turf_items) || !refresh.mob?.listed_turf)
return