Quick answer: Test deep links across two axes: app state (cold start, warm start, already running) and link validity (valid, expired, malformed, wrong account). For each combination, confirm the player lands on the correct screen with the correct context, and that bad links fail gracefully to a sensible fallback instead of a blank screen or a crash.
A share link is a promise. A player taps a friend's invite expecting to land on a specific level, a specific lobby, or a specific reward, and the game has one chance to make good on it. Deep links and share links are deceptively hard to test because they cross the boundary between the operating system, your launcher, and your game's navigation stack. They behave differently depending on whether the game is already running, freshly launched, or backgrounded. This post lays out a concrete QA pass that covers link routing, cold and warm starts, and the invalid link cases that quietly send players nowhere.
What actually breaks with deep links
Deep links fail in clusters, and the clusters are predictable once you have seen them. The most common is the cold start race: the link fires before your game has loaded its session, so the router has no player context to route against and silently drops the player on the home screen. The second is the warm start case, where the game is already running and the incoming link has to interrupt or replace the current navigation stack without losing unsaved state.
The third cluster is data: a link encodes an identifier, a level, or a referral code, and that payload arrives malformed, expired, or pointing at content the current player cannot access. Each of these is invisible in a happy-path demo and obvious in production, because real players share links across accounts, time zones, and app versions. A QA pass that only tests one healthy device misses all three.
Cold start versus warm start
Cold start means the game process is not running when the link is tapped. The operating system launches your game, and the link payload has to survive all the way through your splash, your authentication, and your initial data load before the router can act on it. The classic bug is that the payload is read too early and discarded, or read too late after navigation has already settled on a default screen. Test cold start by force-quitting the game completely before every tap, not just backgrounding it.
Warm start means the game is already in memory, either foregrounded or backgrounded. Here the risk is the opposite: the router acts immediately but stomps on whatever the player was doing, or it queues the link and never processes it because the foreground transition swallowed the event. Test both sub-cases deliberately. Background the game, tap the link, and confirm it resumes to the right place. Then keep it foregrounded, tap a link from another app, and confirm the same.
Link routing and payload validation
Routing is where intent meets your navigation code. Build a small matrix of every link type you support: invite, level, store item, referral, password reset, and so on. For each, confirm the link lands on the exact screen, with the exact parameters, and that any required preconditions are met first. A level link should not skip a mandatory tutorial gate, and a store link should not open a checkout for an item the player already owns. Write these as explicit cases, not as a vibe check.
Payload validation is the unglamorous half. Decode the link in a debugger and confirm the parameters match what the link generator emitted. Watch for encoding mistakes: spaces, plus signs, and non-ASCII characters in player names are a frequent source of corrupted payloads. Confirm that you validate the payload on arrival rather than trusting it, because share links travel through messaging apps that helpfully rewrite, truncate, or append tracking junk to your URLs.
Invalid, expired, and cross-account links
The cases players hit most are the ones developers test least. An expired invite, a link to a lobby that already filled, a referral code that was already redeemed, a link generated by a newer build than the one installed. For each, decide the intended behavior in advance and then verify it. Expired should explain itself and offer a next step. A full lobby should route to a relevant nearby screen, not a dead end. A redeemed code should say so plainly rather than silently doing nothing.
Cross-account links deserve special attention. A player taps a link meant for a friend's account, or taps their own link while signed into a different profile. Your game must not leak the other account's data and must not crash trying to load content the current player has no entitlement to. The correct outcome is almost always a clear message and a safe fallback to the home screen, with the failed link captured so you can see how often it happens.
Setting it up with Bugnet
Link bugs are hard to reproduce because the report rarely includes the link, the app state, or the device that failed. Bugnet's in-game report button captures game state automatically, so when a player taps an in-game report after a broken share link, you get the encoded link payload, the navigation stack, the player's account context, and the platform and build all attached to one report. That turns a vague that link did nothing into a reproducible case with the exact parameters in hand.
If a malformed link causes a crash on cold start, Bugnet's crash reporting captures the stack trace and device context so you can see whether the failure is in your URL parser, your router, or your session load. Occurrence grouping folds the dozens of duplicate reports from one bad link template into a single issue with a count, so you can tell a one-off typo from a systemic routing bug. Custom fields let you tag reports by link type and route through your dashboard in one place.
Building a repeatable link QA pass
Turn the matrix into a checklist you run every release: each link type, crossed with cold start, warm foreground, and warm background, crossed with valid and at least two invalid payloads. It looks like a lot, but most of it is fast once you have the test links prepared. Keep a stash of pre-generated links, including deliberately broken ones, so you are not minting fresh links by hand under deadline pressure on release day.
Automate what you can. A simple script that fires intents or universal links into the device can cover the routing matrix far faster than thumbs on glass, leaving manual testing for the judgment calls about fallback behavior. The goal is not zero link bugs forever, it is that the predictable clusters are caught before players do, and that the rare ones surface as clean reports instead of silent disappointments your community quietly stops mentioning.
Deep link bugs hide on the happy path. Test app state and link validity as two separate axes, and your link matrix will catch what demos never do.