Troubleshooting

Most surprises in a first report are Craft working as designed. Here are the ones people hit.

Findings that aren’t faults

Thousands of Matrix blocks with empty keywords

Normal. Nested elements usually index to empty rows of their own, because their content reaches search through the element that owns them. Holmes reports these as a notice rather than a warning for exactly this reason — on a real site there are often more of them than there are entries. If they still clutter your report, add empty-keywords to disabledChecks, or filter the findings with EVENT_DEFINE_ISSUES.

Trashed elements still have index rows

Also normal, and deliberate on Craft’s part — a restore has to bring the element back searchable. Craft’s own definition of an orphaned row is one with no elements_sites row, which trashed elements still have. Holmes reports the count as a notice and leaves them alone.

A recurring event reported once, not twenty-five times

Deliberate. Some element types return more than one instance per element ID — Solspace Calendar expands a recurring event into one instance per occurrence, all sharing an ID and a single index row. Holmes deduplicates on element and site, and only reports if no instance agrees with what’s stored.

Rows a plugin deliberately suppresses

A plugin can drop index rows through Craft’s beforeIndexKeywords event, and some do — Formie drops empty ones. Holmes fires the same event when it recomputes, so it expects exactly the rows Craft would write and does not report those as missing. If you see drift you believe is wrong on a site with such a plugin, check whether that handler has side effects; Holmes calls it during audits.

Audits

A deep audit never finishes

It is loading every element it is allowed to. Check deepLimit — if it is 0, the audit is uncapped, which on a large site is a long job by design. Bring it back to a few thousand for routine runs and save uncapped ones for a nightly job. Lowering batchSize helps memory, not runtime.

A deep audit times out in the browser

It shouldn’t — queueDeepAudits is on by default and pushes it to the queue. If it was turned off, turn it back on. Web requests are not the place to load a hundred thousand elements.

The report says “and 4,318 more”

Working as intended. Each check records at most maxIssuesPerCheck findings, 500 by default, and counts the rest. The count is what you act on, and a bulk fix rediscovers its own scope rather than working from the list — so a capped report still repairs everything.

The same drift comes back after every fix

Something is rewriting those rows, or the recomputation genuinely disagrees with what Craft writes. Turn on traceIndexWrites and watch storage/logs/holmes.log while you save the element: you will see every write Craft makes to the index and what triggered it.

If the log shows Craft writing exactly what Holmes expected and the audit still reports drift on the next run, reindex that one element with Craft’s own indexer and compare again. If Holmes still disagrees after that, the recomputation is wrong rather than the site — that’s worth an issue, with the element type and the field involved.

Repairs

The pending queue count never goes down

Check the reserved figure. A worker that died holding a job leaves its claim on those rows permanently, and Craft’s indexer will skip them forever — there is no interface in Craft for clearing it. holmes/sync/queue, or the Sync screen, releases reserved rows before draining.

Reindexing several sites made several queue jobs

Expected. Craft’s UpdateSearchIndex job takes one site ID or '*', never a list, so a run across named sites becomes one job per site.

Truncated keywords come back truncated

Because they are. Reindexing writes the same value and Craft cuts it off at the column’s capacity again. There is no fix on the Holmes side: split the field, make the longest fields non-searchable, or raise the column’s capacity in the database. The check exists because the failure is otherwise completely silent.

Search console

Every term says LIKE

Look at the reasons column. Short terms, stop words, phrases, leading wildcards, exclusions and exact matches all bypass the full-text index — that is Craft’s behaviour, not a misconfiguration. It does explain why a particular query is slow on a large index.

The same query behaves differently in two sites

Keywords are normalized in each site’s own language, so accents, casing and word splitting can differ. Run the query against each site in turn and compare the rows that matched.

Coverage looks wrong

Coverage counts elements with any index row. An element with a title row and no field rows counts as covered, so a site at 100% coverage can still have a real problem — that is what the partly indexed elements check is for. Conversely, coverage well below 100% on a type that should be searchable almost always means an import that bypassed Craft’s save.

Still stuck?

Open an issue on GitHub. The useful things to include are your Craft and Holmes versions, the database and its version, the check that is misbehaving, and — for a drift report you think is wrong — the element type and the field.