AACsearch
Fehlerbehebung

Empty results

Search returns `found: 0` for queries that should match — diagnose missing documents, narrow filters, scoped-token filters, and `queryBy` mismatches.

Empty results almost always come from one of four causes. Run the checks in order.

Symptom

{
	"hits": [],
	"found": 0,
	"page": 1,
	"perPage": 10,
	"facetCounts": []
}

A 200 with found: 0 is not an error. It means "request was valid, no documents matched."

Decision tree

1. Is the index actually populated?
   - Search → Indexes → look at "Documents" count
   - If 0 → see [Ingest failures](/troubleshooting/ingest-failures)

2. Does the query field exist on the documents?
   - queryBy lists fields to search
   - If you queryBy a field that does not exist, no document can match

3. Are filters too narrow?
   - Drop filterBy entirely and retry
   - If results return now, the filter was the cause

4. Is a scoped token AND-combining a hidden filter?
   - Scoped tokens always AND-combine their scopedFilter
   - Decode the token to see what is being added

Check 1: index has documents

curl -X GET https://app.aacsearch.com/api/v1/projects/<projectId>/indexes \
  -H "Authorization: Bearer aa_admin_..."

Look for the documentCount field on the matching index. If 0, ingestion has not happened or has failed.

You can also check from the dashboard: Search → Indexes → "Documents" column.

→ If 0, jump to Ingest failures.

Check 2: queryBy matches indexed fields

queryBy is a comma-separated list of the fields the search engine scans. A field must exist in the index schema and be of a string-like type.

// ❌ This never matches anything if the field is `name`, not `title`
client.search({ q: "shoes", queryBy: "title" });

// ✅ Match the actual field
client.search({ q: "shoes", queryBy: "name,description" });

Inspect the schema:

const indexes = await admin.listIndexes();
const products = indexes.find((i) => i.slug === "products");
console.log(products.fields);
// → look for which fields have type "string" or "string[]"

Check 3: filters narrow it to nothing

Strip filterBy and re-run:

// Original — returns 0
await client.search({
	q: "shoes",
	queryBy: "name",
	filterBy: "in_stock:=true && price:<50 && brand:=Nike",
});

// Remove filters
await client.search({ q: "shoes", queryBy: "name" });
// → 247 results

// Add filters back one at a time to find the offender

A common cause is comparing a string facet with a numeric operator (:<, :>) — the engine returns 0 because the comparison cannot be evaluated.

Check 4: scoped-token AND-combined filter

If you are using a scoped token, the token's scopedFilter is silently AND-ed with whatever the caller passes. From the request side it is invisible.

Decode the token to see what is being added:

const [, payloadB64] = scopedToken.replace("ss_scoped_", "").split(".");
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
console.log(payload.scopedFilter);
// → "organization_id:=org_abc && availability:=in_stock"

If the token's filter excludes the documents you expect, mint a new token with the right filter (server-side; see Scoped search tokens).

Scoped tokens can only narrow access. The AND-combination is enforced by combineFilters() in packages/api/modules/search/lib/scoped-token.ts — Hard Invariant 4 in the codebase. There is no way to widen a scoped token from the client.

Other causes

Typo tolerance is set to 0

By default, AACsearch allows minor typos. If numTypos: 0 is set on the index or per-request, "shose" will not match "shoes".

client.search({ q: "shose", queryBy: "name", numTypos: 2 });

Stop words filtered the entire query

Common words ("the", "and") are dropped before search. A query of only stop words returns nothing. Add a more specific term.

Synonyms not loaded

If you expect "sneaker" to match "shoe", verify the synonym set is published:

const synonyms = await admin.listSynonyms(indexId);
console.log(synonyms);

→ See Search API → multi-search and querying for the full synonym configuration.

Recently ingested but not yet visible

Documents go through SearchIngestBuffer before they reach the live index. Typical lag is < 30 seconds; cold-start can be longer. If you just pushed, wait and retry.

# Check the buffer queue depth (admin API)
curl -X GET https://app.aacsearch.com/api/v1/projects/<projectId>/usage \
  -H "Authorization: Bearer aa_admin_..."

Fix matrix

DiagnosisFix
Index emptyPush documents — see Ingest failures
queryBy field does not existUse a real field; check schema
Filter too narrowLoosen filterBy; remove and add back one term at a time
Scoped token excludes target docsMint a new scoped token server-side with the right filter
Typo or unusual spellingSet numTypos to 1 or 2
Recent ingest not visibleWait 30 seconds and retry; check buffer queue depth

Diagnostics packet

FieldNotes
Organization IDrequired
Index slugrequired
Full request bodyincluding q, queryBy, filterBy, sortBy
Expected matchone document ID or external_id you expected to see
Document count on indexfrom listIndexes() or dashboard
Scoped token in useyes/no — if yes, decoded payload (without signature)

On this page