Magento development · Indexer debugging

Magento 2 Indexers Stuck on “Processing”? A Developer’s Guide to Debugging

Follow the evidence from worker ownership to database state, cron execution, and storefront results—with practical code you can adapt.

A Magento indexer can show “Processing” while prices remain outdated, search results lag, or category changes never reach the storefront. The tempting response is to reset every indexer and start again. That can hide the original failure, overlap a healthy worker, and create more database pressure.

A better investigation follows one indexer from its displayed status to the process, database activity, scheduled work, and customer-visible result. This guide uses catalog_product_price as a running example. Replace it with the affected identifier and adapt the checks to your Magento Open Source or Adobe Commerce installation.

Before recovery: establish whether work is still running anywhere in the deployment. Read-only inspection comes first; reset and reindex commands change application state and belong in a coordinated recovery window.

1. Understand what “Processing” actually tells you

Magento separates indexer status from the scheduled view that processes changes. Adobe maps the database value working to the Admin label “Processing.” A status row records application state; it is not independent proof that a healthy worker currently owns the job.[1]

Start with the status, then collect supporting evidence
Database valueAdmin labelNext question
validReadyAre scheduled changes and storefront results current?
invalidReindex RequiredWhat invalidated the index, and will a worker rebuild it?
workingProcessingWhich worker is responsible, and is it progressing?

Newer releases also expose operational controls such as suspension. An intentionally suspended indexer needs a different investigation from an abandoned run. Check the installed command help before applying instructions copied from another release.[2]

Do not use elapsed time alone as the verdict. A large price index across many websites and customer groups may legitimately take longer than yesterday’s smaller catalogue. Compare with previous successful runs and look for evidence of forward progress.

2. Capture the baseline before changing anything

Run Magento CLI commands from the active application release as the filesystem owner. List the available identifiers, inspect the affected indexer, and record its update mode.[2]

Application root · identify the release and indexerInspectionbash
# Run as the Magento filesystem owner in the active release.
php bin/magento --version
php bin/magento indexer:info
php bin/magento indexer:status catalog_product_price
php bin/magento indexer:show-mode catalog_product_price

Save the environment name, release identifier, observation time, indexer ID, and most recent successful completion. Ask what changed immediately before the symptom: a deployment, import, extension update, configuration change, or worker restart. A precise timeline makes unrelated warnings easier to discard.

Keep timestamps in a consistent timezone and distinguish observation time from failure time. Preserve the relevant deployment and scheduler records before routine retention removes them. When handing the investigation to another engineer, include the exact identifier and release path; the label “price indexing is broken” leaves too many assumptions unresolved.

Confirm that CLI PHP matches the binary used by the scheduled worker. An interactive shell may select another version or configuration file. Use the actual worker binary for these checks when its path differs.

CLI PHP · compare with the scheduled workerInspectionbash
command -v php
php -v
php --ini
php -r 'echo "memory_limit=", ini_get("memory_limit"), PHP_EOL;'

Compare the incident with a healthy indexer and a previous successful run of the same indexer. If several unrelated jobs stopped together, investigate shared scheduling or infrastructure. If one indexer repeatedly fails while others finish, focus on its data, dependencies, and customizations.

3. Find the worker and classify its condition

Inspect processes on the host or container responsible for background work. Include cron parents, child PHP processes, custom wrappers, and workers on other nodes. This Linux command is a starting point, not a complete inventory of a distributed deployment.

Linux worker host · repeat on relevant nodesInspectionbash
ps -ww -eo user,pid,ppid,etime,pcpu,pmem,args \
  | grep -E '[p]hp|[m]agento'

Record the PID, parent PID, runtime, and command. Compare multiple observations with application logs and database activity. CPU usage alone is inconclusive: a worker can consume little CPU while waiting for I/O or a database lock, and heavy CPU usage does not prove useful progress.

Choose recovery from worker evidence
What is happening to the affected run?
Active and progressing
Measure workload and completion trend
Active but waiting
Inspect database and downstream waits
No confirmed worker
Check other nodes and termination evidence

An empty local process list cannot rule out a remote worker. Confirm ownership through the scheduler or process manager before resetting state. If termination is necessary, coordinate it with the operations owner and preserve the failure evidence first.

Check whether the parent has exited while a child process remains active, and whether a deployment left a worker attached to an older release. Also identify duplicate scheduling entry points. Two teams can each start a legitimate recovery command, creating a conflict that looks like the original failure.

4. Compare indexer and materialized-view state

Query the intended database using a read-only session. Add the configured table prefix to every table name below. The price indexer and its view share an identifier in this example, but that is not universal: inspect view_id in the module’s etc/indexer.xml and its corresponding etc/mview.xml.[3]

Application database · add your table prefixRead-only SQLsql
SELECT indexer_id, status, updated
FROM indexer_state
WHERE indexer_id = 'catalog_product_price';

SELECT view_id, mode, status, version_id, updated
FROM mview_state
WHERE view_id = 'catalog_product_price';

Interpret indexer_state and mview_state separately. The view’s mode describes its subscription state, not the CLI labels for update mode. Its version_id is a processing checkpoint. An old updated timestamp is useful context, but neither timestamp is a reliable live heartbeat.

With application locking enabled, Adobe notes that displayed status can differ from raw table values after a failed run.[1] Reconcile database observations with CLI output, worker ownership, and logs before calling a state stale.

5. Verify that indexing cron actually runs

The Indexer module declares separate jobs for rebuilding invalid indexes and updating materialized views. Inspect recent indexer_reindex_all_invalid and indexer_update_all_views schedules to distinguish failed execution from missing scheduling.[4]

Recent indexing schedules · bounded result setRead-only SQLsql
SELECT schedule_id, job_code, status,
       created_at, scheduled_at, executed_at, finished_at,
       LEFT(messages, 1500) AS diagnostic_message
FROM cron_schedule
WHERE job_code IN (
    'indexer_reindex_all_invalid',
    'indexer_update_all_views'
)
ORDER BY schedule_id DESC
LIMIT 40;

Read status together with the scheduled, start, and finish times. Pending work may not be due yet; overdue rows indicate delay. Errors need their original exception. A successful schedule can also have had nothing to process, so verify that the expected change reached its destination.

On a conventional installation, review the filesystem owner’s crontab, scheduler service, PHP command, and release path. Adobe documents cron history in cron_schedule and logs in var/log/cron.log.[5] For Commerce on cloud infrastructure, inspect the platform-managed crons configuration in .magento.app.yaml.[6]

Restore the recurring execution path. A successful manual rebuild will not keep subsequent catalogue changes synchronized if scheduling remains broken.

6. Inspect pending changes without deleting evidence

Scheduled indexing uses change tracking and changelog tables. After confirming that the price view is enabled and its table exists, sample entries beyond the recorded checkpoint. Use the actual view and changelog names for another indexer.[1]

Price view · sample changes beyond its checkpointRead-only SQLsql
-- Confirm the view is enabled and this changelog exists.
-- Apply the configured table prefix to both table names.
SELECT cl.version_id, cl.entity_id
FROM catalog_product_price_cl AS cl
JOIN mview_state AS view_state
  ON view_state.view_id = 'catalog_product_price'
WHERE cl.version_id > view_state.version_id
ORDER BY cl.version_id ASC
LIMIT 20;

Repeat the observation across normal scheduled cycles. A stationary checkpoint with increasing pending work suggests a processing problem. The difference between two version numbers is not an exact count of pending rows or unique products; identifiers can have gaps and entities can recur.

Trace a controlled catalogue change
Where does the expected update stop?
No captured change
Check subscription, triggers, and write path
Pending changes remain
Check cron execution and callback errors
Changes processed
Check indexed result, store scope, and cache

Do not truncate changelog tables or manually advance the checkpoint to clear a backlog. Those changes can discard pending updates while making the status appear healthier. Preserve the evidence and repair the stage that stopped.

7. Identify the failure behind the status

Adobe lists interrupted processes, memory limits, database failures, and PHP errors among causes of an index remaining locked after an unsuccessful run.[7] Correlate the first failure with application, database, and hosting events.

Application logs · use central logs if collected elsewhereInspectionbash
tail -n 120 var/log/cron.log
tail -n 120 var/log/exception.log
tail -n 120 var/log/system.log

Read the relevant time window and full exception, rather than treating the last log line as the cause. Missing local logs may mean that the platform collects them centrally. Check container restarts, worker timeouts, and out-of-memory events with the hosting team.

Database diagnostics · privileges affect visibilityRead-only SQLsql
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS;

Database process information helps identify active queries and waits; visibility depends on account privileges. InnoDB status can reveal transactions and the latest detected deadlock.[8][9] Match timestamps carefully: an old deadlock report may describe another incident.

For search indexing, inspect the configured search service and its response errors. For custom indexers, compare the failing batch with recent module changes. Keep raw query output and logs within the incident record because they can contain operational or customer data.

A custom indexer can succeed during a full rebuild but fail when processing incremental changes. Compare its executeFull() path with the materialized-view execute($ids) path, using representative changed entities in staging.[3] Look for assumptions about deleted records, missing attributes, and empty input. Preserve exceptions instead of swallowing them and reporting an apparently successful update.

8. Recover the affected indexer deliberately

Once no active worker owns the run and the underlying fault is addressed, coordinate a short recovery window. Prevent competing scheduler or import invocations using the platform’s operational controls, allow existing work to finish, and confirm current backups and rollback ownership.

Adobe’s recovery guidance uses a targeted reset followed by reindexing. Reset invalidates the selected status; it neither rebuilds the data nor terminates a live worker.[7][2]

Recovery window · only after worker ownership is resolvedChanges statebash
# Prevent competing work and address the original failure first.
# Reset changes status; it does not stop a live worker.
php bin/magento indexer:reset catalog_product_price
php bin/magento indexer:status catalog_product_price

Confirm the expected invalid state, then rebuild that identifier once. Check the complete command output for exceptions or a skipped indexer. If it still reports another owner, return to ownership and lock diagnosis instead of repeatedly resetting.

Recovery window · one targeted rebuildChanges statebash
# Confirm the selected indexer is ready for recovery.
php bin/magento indexer:reindex catalog_product_price
php bin/magento indexer:status catalog_product_price

Restore every scheduler or import process paused for recovery. Account for delayed emails and other background work when a shared scheduler was paused. Avoid resetting unrelated indexers, forcing status to valid, or deleting lock storage merely to make the status display green.

Review custom dependencies and shared indexes before scheduling parallel recovery work. Indexer configuration can declare a shared_index relationship.[3] Different identifiers therefore do not necessarily represent independent work. Keep the recovery scope documented so another operator knows which jobs must remain coordinated.

9. Review application locking and repeat triggers

From version 2.4.3, application lock mode can improve failure detection and allow cron to retry an indexer whose earlier attempt failed. It does not resolve the error that caused the failure.[1]

app/etc/env.php · merge into the existing returned arrayConfigurationphp
// Configuration fragment; do not replace the whole file.
// If 'indexer' exists, merge the setting into that section.
'indexer' => [
    'use_application_lock' => true,
],

This is an array fragment, not a replacement configuration file. Merge it into the existing configuration, preserving other indexer settings. On managed infrastructure, use the supported deployment configuration workflow. Validate the change in staging before rollout.

Changing an indexer’s mode can create or remove database triggers. Adobe recommends maintenance mode and disabling cron before mode changes.[2] Treat trigger repair as a separate change with validation of newly captured updates. If subscriptions are missing after deployment, investigate schema changes and module configuration before toggling modes, then confirm that fresh catalogue writes appear in the expected changelog during a controlled test.

Also distinguish one abandoned run from continuous invalidation. Adobe documents a Commerce-specific interaction between imports and Category Permissions that can repeatedly invalidate related indexes.[10] Correlate imports with rebuilds before changing custom plugins. Disabling a permission feature to reduce workload can change who sees products.

10. Prove recovery and prevent recurrence

Observe several automatic cycles after recovery. Make a controlled catalogue change in staging and verify the correct website, customer group, currency, and storefront view. Check that the index reflects the change before investigating downstream caching or an external catalogue service.

Track completion time, errors, backlog trend, and business freshness. Choose alert thresholds from normal workload and the store’s tolerance for stale data. An alert should identify the environment, indexer, last successful result, and responsible owner.

Adobe recommends scheduled indexing for large or frequently updated stores. Customer Grid support differs by release: before 2.4.8 it requires Update on Save; from 2.4.8 it also supports scheduling.[11] Review modes as planned configuration work, not as an emergency toggle.

For sustained resource pressure, benchmark appropriate batch sizes and workload scheduling. Adobe provides indexer-specific batching controls.[12] Change one variable at a time and measure both completion time and storefront impact.

Measure PHP memory and database temporary-table requirements separately. Increasing a PHP limit will not resolve database contention, and adding workers can amplify competition for the same resources. Test tuning against representative catalogue size, website count, and customer groups; a small development dataset may conceal the expensive combinations present in production.

For example, if a price change reaches the index but remains invisible only for one customer group, continue through that group’s pricing context and cache behaviour. Repeating the full rebuild without tracing the discrepancy adds workload while leaving the actual customer-facing defect unexplained.