Shopify Development

Mastering Shopify App Bridge Sidekick: Solving the 'Tool Not Found' Race Condition

Diagram showing a timing race condition in Shopify App Bridge Sidekick tool registration and the solution with early inline script
Diagram showing a timing race condition in Shopify App Bridge Sidekick tool registration and the solution with early inline script

Mastering Shopify App Bridge Sidekick: Solving the 'Tool Not Found' Race Condition

As a Shopify app developer, you're constantly pushing the boundaries of what's possible within the Shopify Admin. The App Bridge library, especially its newer features like Sidekick and the intent API, offers powerful ways to integrate your app deeply into the merchant's workflow. However, with new power comes new challenges, and one particularly frustrating issue that can arise is the dreaded "tool not found" error.

At Shopping Cart Mover, we specialize in seamless e-commerce solutions, including advanced Shopify development and integrations. We recently observed a critical discussion in the Shopify Community that perfectly illustrates a common pitfall with App Bridge's `shopify.tools.register` method and full-page navigations. Understanding this issue is crucial for building robust and reliable Shopify apps.

The Problem: A 'Tool Not Found' Mystery

Our fellow developer, blueliner, encountered a classic race condition while implementing `sidekick-import` with `admin.app.intent.link`. The scenario was straightforward: a merchant, while using another embedded app (e.g., an eBay Importer), would trigger an intent that caused a full-page navigation to blueliner's app. The expectation was that Sidekick would then call their registered tool, `preview_amazon_product`, to perform an action. Instead, Sidekick returned a "tool not found" error.

What made this particularly puzzling was that the tool *did* register successfully, as confirmed by logs, but only after a significant delay. The tool worked perfectly in other scenarios: when the merchant was already on a native Shopify Admin page or already inside blueliner's app. The failure occurred specifically after a full-page navigation from *another* embedded app.

The Smoking Gun: A Timing Race

The crucial piece of information blueliner provided was that their tool registration was happening approximately 2380 milliseconds (2.38 seconds) from page load. This, as Mindaugas_LM astutely pointed out, was the "smoking gun."

Here's why this delay is critical:

  • Full-Page Navigation Reloads Everything: When an intent triggers a full-page navigation, your app reloads from scratch. This means your entire JavaScript bundle needs to be downloaded, parsed, and executed again.
  • Sidekick is Eager: Sidekick, part of the Shopify Admin, dispatches the tool call almost immediately after the navigation completes. It doesn't wait for your app to fully boot up.
  • Bundle Load Time is Your Race Window: Those 2380ms represent your app's bundle parse and evaluation time. Even if your `shopify.tools.register(...)` call is at the very top of your entry file, module-level, and before React mounts, the entire bundle must process first. During this window, Sidekick calls for a tool that simply hasn't been registered yet. The `shopify.tools` API, especially for Sidekick intents, does not queue incoming calls; it drops them if the tool isn't immediately available.

The Elegant Solution: Decoupling 'Tool Exists' from 'Tool is Ready'

The fix lies in decoupling when the tool is *registered* from when it's *ready to perform its work*. The goal is to make the tool exist almost instantly, even if its underlying logic and dependencies are still loading.

Mindaugas_LM's recommended approach is to register a thin wrapper for your tool outside your main JavaScript bundle, in a tiny inline

  • Add Early Inline Registration: Immediately after the App Bridge CDN script, add an inline

    This code does two critical things:

    • It creates a global Promise, `window.__appReady`, which will resolve when your main app bundle is fully loaded and ready.
    • It registers your tool, `preview_amazon_product`, immediately. The handler for this tool, however, `awaits` the `window.__appReady` Promise. This means the tool is *registered* and *exists* for Sidekick, but its actual execution is buffered until your app is fully operational.
  • Resolve the Promise in Your Main App Bundle: Once your main app (e.g., your React or Vue app) has fully booted, its Redux store is ready, and your API clients are initialized, you call `window.__resolveAppReady()` to signal readiness.
    // Inside your main app bundle (e.g., index.js, after React mounts and dependencies are ready)
    // ... after Redux/api are ready
    if (window.__resolveAppReady) {
      window.__resolveAppReady({
        previewAmazonProduct: (input) => {
          // Your actual tool logic, e.g., calling an API and dispatching to Redux
          const result = await previewProductApi(input.amazonProductUrl);
          // ... dispatch to Redux store
          return {ok: true, title: result.title};
        }
      });
    }

    This resolves the Promise, allowing any buffered Sidekick calls to proceed with their actual work.

  • Diagnostic Timestamps (Optional but Recommended): For robust debugging, add high-resolution timestamps (`performance.now()`) at both the inline register line and when your actual handler fires. This helps confirm the timing and diagnose any lingering issues.
  • Why This Works and Best Practices

    This solution effectively separates the concerns of tool registration (which must be instant) from tool execution (which can be deferred). It ensures that Sidekick always finds a registered tool, preventing the "tool not found" error, while gracefully handling the app's boot-up time.

    Key Best Practices for Shopify App Developers:

    • Register App-Wide: Always register your tools globally at app bootstrap, not within specific components or routes, to ensure availability across all entry points.
    • Prioritize App Bridge Loading: Ensure the Shopify-hosted App Bridge CDN script is loaded as early as possible in your HTML ``.
    • Consider Session Mismatch: If, after implementing this fix, you *still* encounter issues, it might indicate a deeper session/instance mismatch where Sidekick is calling into a different App Bridge context than the one your page registered on. This is a platform-level behavior that would warrant a direct report to Shopify Partner support.

    Building Reliable Shopify Integrations

    The Shopify platform offers incredible flexibility for merchants, and well-integrated apps are at the heart of that experience. Understanding these subtle timing nuances in App Bridge is crucial for building apps that are not just functional but also reliable and performant. For developers looking to build powerful, integrated apps on the Shopify platform, or for merchants considering to start your Shopify store today, these technical insights ensure a smoother journey.

    At Shopping Cart Mover, we're committed to helping businesses leverage the full potential of Shopify. From complex migrations to custom app development and integration, our expertise ensures your e-commerce operations run flawlessly.

    Share:

    Use cases

    Explore use cases

    Agencies, store owners, enterprise — find the migration path that fits.

    Explore use cases