Steamworks can initialize successfully while callbacks still never reach your game. In the Unity Editor this often looks like achievements that never toast, lobby invites that never arrive, or overlay messages that never trigger handlers, even though SteamAPI_Init returned true.
This article assumes native load and steam_appid.txt are already plausible. If initialization itself fails, start with Steamworks Init Failed in Unity Editor. If unlocks fail in packaged builds specifically, pair this guide with Steam Achievements Not Unlocking in Unity Build.
Problem summary
Typical symptoms
- You register callbacks (Steamworks.NET, Facepunch.Steamworks, or raw
SteamAPI_RegisterCallback) and never see handlers fire in Play Mode. - The Steam client is running, Init succeeds, remote actions happen on another account, but your local session stays silent.
- Behavior improves when you alt-tab back into the Editor or click the Game view.
When it shows up
- After integrating Steamworks and focusing tests on standalone builds, while Editor testing is an afterthought.
- When
SteamAPI_RunCallbacksis only called fromFixedUpdateor from a slow coroutine. - When Run In Background is disabled and the Editor loses focus during multiplayer or remote tests.
Impact
- False negatives in Editor QA, so bugs slip to builds or live.
- Teams rewrite networking or UI code when the real issue is pump timing.
Why this happens
Steam’s API is pull-driven for many notifications. If your process does not call SteamAPI_RunCallbacks regularly, pending work queues up and your delegates never run.
Common root causes in Unity:
-
Callbacks not pumped every frame
RunCallbacksis missing, behind atrythat swallows errors, or only runs when certain scenes load. -
Editor focus freezes the player loop
With Run In Background off, switching to Visual Studio, Slack, or a browser pauses Play Mode. Callbacks stop pumping even though Steam is still generating events. -
Init order
Callback objects are created beforeSteamAPI_Init, or are destroyed during domain reload while Steam still references stale handles. -
Wrapper-specific expectations
Some wrappers batch work and still require periodicRunCallbacksor their ownUpdatehook. Skipping that hook looks like “Steam is broken”.
Fix steps (do them in order)
Step 1 - Enable Run In Background
In Unity: Edit → Project Settings → Player → Resolution and Presentation → Run In Background. Enable it for the configuration you use in Play Mode (PC, Mac, Linux standalone settings still affect Editor behavior for this flag in many Unity versions).
Re-test the exact scenario where callbacks failed while you were not focused on the Game view.
Step 2 - Pump callbacks on the main thread every frame
Add a small bootstrap component that runs in Update (not only FixedUpdate):
- Guard with your wrapper’s “initialized” check.
- Call
SteamAPI_RunCallbacksexactly once per frame on the main thread. - Log once per session (not spammy) the first time
RunCallbacksruns after init so you can confirm order in the Console.
If you use Steamworks.NET, follow its documented CallbackManager.RunCallbacks pattern in Update. If you use Facepunch.Steamworks, follow its Update hook guidance for the version you pinned. The invariant is the same: steady pump, main thread.
Step 3 - Initialize once, before registering long-lived callbacks
Order should look like:
- Create or verify
steam_appid.txtfor editor tests (see init article). - Call Init and confirm success.
- Register callbacks and achievement/stats interfaces.
- Start pumping.
If you register first, some wrappers appear fine until the first remote event exposes undefined ordering.
Step 4 - Survive domain reload and re-enter Play Mode
If you use Enter Play Mode Options without domain reload, static callback holders can survive in a bad state.
Pick one policy:
- Full domain reload for Steam-heavy scenes while iterating, or
- Explicit Shutdown on play mode exit and re-Init on next enter, documented in your bootstrap.
Add RuntimeInitializeOnLoadMethod or EditorApplication.playModeStateChanged hooks only if you understand double-init risk. The default safe path for small teams is quit Play Mode fully between Steam config changes.
Step 5 - Confirm you are not testing impossible editor paths
Some interfaces are build-only or behave differently in Editor. When a callback never fires, verify in Steam’s partner documentation whether that interface is supported in development contexts. Link your expectations in QA docs so Editor skips do not get misfiled as code bugs.
Verification checklist
- With Run In Background enabled, unfocus the Editor for thirty seconds while a remote friend triggers an action that should notify you. Callbacks still arrive.
- Console shows your one-time “pump started after init” log in the correct order.
- After ten minutes of idle gameplay, trigger another callback event. Still works (catches throttled
Updateremovals). - Standalone Development build still receives callbacks (guards against Editor-only false positives).
Alternative causes to scan quickly
- Editor vs build define symbols accidentally excluding your pump script in Editor.
- Multiple bootstrap objects calling Init or
RunCallbackstwice per frame (rare, but causes hard-to-read races). - Exception inside a callback stopping subsequent pumps if your wrapper disables itself on error.
Prevention tips
- Keep a single
SteamBootstrapMonoBehaviour responsible for Init, pump, and shutdown. - Add a lightweight Editor window debug readout showing last pump time and last callback category (no secrets).
- Document for QA: “Editor tests require Run In Background on” next to your Steam test matrix.
FAQ
Should RunCallbacks run in LateUpdate instead of Update?
Either is acceptable if it is once per frame and main-thread. Update is the most common choice so gameplay reads Steam state before this frame’s simulation.
Does IL2CPP change Editor pumping?
Editor uses Mono; IL2CPP matters for builds. Still verify a Development build because stripping or link steps can remove a pump script if referenced only weakly.
Is a zero-length interval coroutine enough?
Prefer Update. Coroutines can be delayed by time scale, disabled objects, or scene pauses.
Related links
- Steamworks Init Failed in Unity Editor - Native Plugin Path and Platform Fix
- Steam Achievements Not Unlocking in Unity Build - App ID and Callback Initialization Fix
- Official Steamworks documentation for API overview and callback behavior on the Steamworks API reference (search for
SteamAPI_RunCallbacksfrom the doc index).
Bookmark this page if your team repeatedly blames “Steam delay” when the Editor simply stopped pumping while you were reading logs in another window.