Quick answer: GameMaker Async Image Loaded event firing while the sprite is still null? Async loading reports success before sprite_add returns - check sprite_exists in the handler.

Race condition: handler reads self.sprite_index on the async event but sprite_add hasn't returned yet.

Validate in handler

if sprite_exists(async_load[? "id"]) {
  sprite_index = async_load[? "id"];
}

Check before assignment. Spurious-trigger safety.

Or store the request ID

Match the async event to your original request via ID. Stale events from cancelled loads are ignored.

Use awaitable wrapper

Wrap sprite_add_async in a script that returns when the actual sprite is available. Caller doesn't deal with the async event.

“Async loading reports completion early. Polling for existence is the safety.”

Hide async details behind a Promise-style wrapper. Callers get a 'when ready' callback; the async event handling is centralized.