Wait for SDK Ready
QuestData runs as a Godot autoload and finishes its sync init in _ready(). If your own autoloads call into the SDK from their _ready(), Godot’s autoload order decides who runs first — and you may race against the SDK. This page shows the one pattern that eliminates that race.
The Pattern
Section titled “The Pattern”# In your own autoload or any Node._ready()func _ready() -> void: if not QuestData.is_ready(): await QuestData.sdk_ready # From here on it's safe: all sub-modules initialized, player-ID known QuestData.track("game_start", { "version": "1.0.0" })…or the one-liner via the idempotent helper:
func _ready() -> void: await QuestData.await_ready() QuestData.track("game_start", { "version": "1.0.0" })await_ready() returns immediately when the SDK is already up, otherwise it blocks until the sdk_ready signal fires. Either form is fine — pick what reads better in your codebase.
Why You Need It
Section titled “Why You Need It”QuestData’s _ready() does a non-trivial amount of work:
- Config load — parsing
quest_data/api_key,api_url, feature flags fromProjectSettings - Player-ID resolve — disk read of the persistent
user://quest_data_player_idfile - HTTP / Timer / Logger setup — child nodes added to the scene tree
- Game-data cache rehydrate — loading the last known balancing tables from disk so the game has values even when offline
session_startevent — first event of the session
If your autoload sits before QuestData in the autoload order (or both run in the same frame and yours happens to win), API calls like track(), bind_balancing(), get_game_data() will hit a half-initialized SDK. In debug builds this crashes hard via assert(false, …) so you catch the bug on the first run. The fix is always the same: wait for sdk_ready.
When You Don’t Need It
Section titled “When You Don’t Need It”Code paths that always run after frame 0 are already safe — the SDK has finished initializing by then:
_process(delta),_physics_process(delta)_input(event),_unhandled_input(event)- Signal handlers connected at runtime (button clicks, timer timeouts, networking callbacks)
- Anything called from gameplay scripts that load after the main scene boots
The pattern is only needed at the boundary: autoload _ready(), the first frame of the main scene, or any code that may race the SDK’s init.
Debug: dump_state().sdk_ready
Section titled “Debug: dump_state().sdk_ready”When a bug report says “events aren’t tracked”, ask the reporter to log the SDK’s init section:
print(JSON.stringify(QuestData.dump_state().sdk_ready, " "))Sample output:
{ "is_ready": true, "sdk_ready_emitted": true, "init_completed_at": 1731504732.451, "seconds_since_init": 47.2, "sub_modules_initialized": { "config": true, "player": true, "session": true, "http": true, "event_queue": true, "game_data": true, "balancing": true }}If is_ready is false or any sub_modules_initialized.* flag is false, the SDK never finished its boot — likely an autoload-order issue or a fatal config error. Open the Godot Output + Debugger → Errors panels for the underlying cause.
Related: Bind-Time Validation
Section titled “Related: Bind-Time Validation”If you register Resource-based balancing overrides via bind_balancing() / bind_balancing_pivot(), the SDK silently fails on typos in table, id_column, row_id, pivot_key_col or pivot_value_col — your .tres defaults stay active and you wonder why nothing changes.
After sdk_ready and a 5-second grace window, dump_state().balancing.bindings_with_issues lists every problematic binding:
print(JSON.stringify( QuestData.dump_state().balancing.bindings_with_issues, " "))The same diagnostic also fires as push_warning(...) at bind-time when the referenced table is already cached — you’ll see the warning in the Godot console the moment you call bind_balancing() with bad arguments. Categories: table_not_in_schema, column_not_found, row_not_found, never_applied.
See Also
Section titled “See Also”- Godot SDK Setup — install + configure the plugin
- Troubleshooting — broader SDK debugging
- Diagnose Fetch-Failures — async callbacks with success/error info for
get_game_dataandfetch_remote_config - Live Balancing — the
bind_balancingconnect pattern in context