Predictive Search Returned Nothing for a Product That Exists
A merchant reported that a hero product did not appear when typing its name into the search box, even though the same name on the full search page returned it first. Two different search systems, two different answers, and the faster, flashier one looked broken.
It was not broken. The predictive search endpoint applies availability and publication rules by default, and the product in question was tripping one of them in a way the full page search was not. The theme was then rendering an empty result as a silent blank box, which read as a bug to everyone but the API.
This was on an Online Store 2.0 theme calling the predictive search route, observed in Chrome 133.
The two search systems
Shopify storefronts typically have two search surfaces. The full search page, which runs a query and returns a results page, and the predictive or suggest endpoint, which returns a small JSON payload as the user types, meant for the dropdown.
They are not the same query with different formatting. The predictive endpoint has its own defaults about which resources to include and which products to exclude, and those defaults are where the disagreement lives.
Why an existing product is absent
A few specific rules will hide a product from suggestions while the full page still shows it.
Availability. Predictive search excludes products that are not available, and the definition of available can differ from what the full page tolerates. A product whose variants are all out of stock, or whose inventory policy marks it unavailable, drops out of suggestions. The full page may still list it because it renders out of stock items.
Publication and channel. The suggest endpoint returns products published to the online store channel. A product published to a POS or other channel but not to online store, or one hidden from a market the request implies, is absent.
The default resource list. The endpoint returns several resource types, products, collections, pages, articles, and the theme may be requesting a subset or reading only one key. If the theme reads resources.products but the product matched as an article or a collection, the box looks empty even though the payload is not.
Status and hidden flags. A product with a status other than active, or one with the hidden from search style settings, is excluded.
The merchant's product turned out to be a combination: it was active and in stock, but its only variant was set to not be tracked with zero quantity in a way that flagged it unavailable to the suggest index, while the full page search, which the theme configured to show out of stock, still listed it.
Seeing what the endpoint actually returns
Stop trusting the rendered box and read the payload:
fetch("/search/suggest?type=product&q=aura+lamp&resources[type]=product")
.then(r => r.json())
.then(d => console.log(d.resources.products.map(p => p.title)));
If the payload contains the product but the box is empty, the defect is in the theme's rendering, and you have a JavaScript problem. If the payload lacks it, the defect is in the rules above, and you have a catalogue problem. That single fetch splits the investigation in half, which is the same discipline as reading the raw response in the Section Rendering API returning yesterday's cart.
Then check the product against each rule: status active, published to the online store channel, available in the market, at least one variant purchasable.
The fixes
Make the two surfaces agree on policy. Decide, as a product decision, whether out of stock items should appear in suggestions. If yes, you must change how the theme treats the payload, because the endpoint's availability filtering is not a theme setting. Some teams accept the difference and label the suggest box as "available now". Others route the suggest box through a custom search that mirrors the full page rules.
Render the empty state honestly. A blank dropdown is the worst outcome because it reads as broken and gives the user no information. Render "no matches" distinctly from "still typing", and include a link to the full search for the same term. The full search is the fallback that already behaves the way the merchant expects.
Log the disagreement. In development, assert that the top result of the full search appears in the suggest payload for the same query on a sample of real queries. When they disagree, you want a red test, not a merchant ticket.
Beware the caching layer. If you put the suggest endpoint behind a cache, remember it is per query string and per market, and a cached empty answer for one market can be served to another. This is the cache key lesson from the cache that returned another customer's data, scoped to markets instead of accounts.
The wider pattern
This is the recurring theme of two systems that look like one. The suggest box and the search page are separate indexes with separate rules, and any storefront that presents them as a single search to the user inherits the obligation to keep their policies aligned. The user does not know there are two systems. They know that the store disagreed with itself.
The same shape appears with collections versus search, with metaobject driven merchandising versus the catalogue, and with any fast path that is a filtered view of a slower truth. The fast path is allowed to be narrower, but the narrowness must be a decision, not an accident.
The rule
When a fast suggest surface and a full search surface disagree, read both payloads before blaming either. The difference is a policy, and the fix is to make that policy explicit in one place, then render every empty answer as an intentional statement rather than a silent blank.
If your search problems are on the performance side rather than the correctness side, the Liquid scanning version is the metaobject query that scanned every entry, and the N plus one variant is shopify liquid n plus one performance.