Skip to main content

September 17, 2026

Algolia v5 replaceAllObjects false-timeouts

Algolia v5 catalog rebuilds false-fail when replaceAllObjects exhausts fifty waitForTask polls. Large indexes need a custom copy, batch, and move path.

Sander Korf3 min read

I maintain search for a fashion catalog storefront. Nightly jobs and on-demand scripts rebuild the full Algolia index: copy settings to a temporary index, batch every product record, then atomically move the temp index over the primary name shoppers hit. When the catalog grows, the job starts failing with timeout errors while the Algolia dashboard still shows healthy tasks chugging along. The script looks broken. The index is often fine.

That mismatch sent me down the wrong path first. I bumped Node timeouts and retried the whole script. Same false failure. The server was not slow. It stopped waiting too early. CI marked the nightly rebuild red while the Algolia console showed copy and batch tasks still in progress. Rerunning immediately made things worse: a second script fought the first temp index until someone read task ids instead of stderr.

v5 hardcodes fifty polls inside the helper

In algoliasearch JavaScript v5, replaceAllObjects wraps copy, batched saveObjects, and move. Between each step it calls waitForTask with maxRetries fixed at 50. That constant is not exposed on the helper options. Large catalogs exhaust fifty polls during a single long task. The client throws. Algolia keeps working.

Each poll is a short sleep and a status check, not a hard wall-clock cap by itself. On a fat index one saveObjects batch can outlive fifty checks when the cluster is busy. The helper gives up. Your job exits non-zero. Shoppers still see yesterday's index until move finishes without you in the loop.

InstantSearch tuning and search-only keys are a different story. This post is about full-index rebuilds that use the v5 helper as a one-liner.

Split default path from long rebuild path

I wrapped replace-all in our own function with an optional waitMaxRetries. Small dev indexes keep calling the SDK helper unchanged (still fifty). Production rebuilds take a manual path that mirrors the helper: copy settings with operationIndex, batch saveObjects, move with operationIndex, passing maxRetries: waitMaxRetries into every waitForTask.

type ReplaceAllOptions = {
	indexName: string;
	objects: Record<string, unknown>[];
	waitMaxRetries?: number; // omit → SDK helper (50 polls inside replaceAllObjects)
};
 
async function replaceAllCatalog(client, { indexName, objects, waitMaxRetries }: ReplaceAllOptions) {
	if (waitMaxRetries === undefined) {
		return client.replaceAllObjects({ indexName, objects });
	}
 
	const tmp = `${indexName}_tmp_${Date.now()}`;
	const copyTask = await client.operationIndex({ indexName, operation: 'copy', destination: tmp });
	await client.waitForTask({ indexName: tmp, taskID: copyTask.taskID, maxRetries: waitMaxRetries });
 
	const saveTask = await client.saveObjects({ indexName: tmp, objects });
	await client.waitForTask({ indexName: tmp, taskID: saveTask.taskID, maxRetries: waitMaxRetries });
 
	const moveTask = await client.operationIndex({ indexName, operation: 'move', destination: tmp });
	await client.waitForTask({ indexName, taskID: moveTask.taskID, maxRetries: waitMaxRetries });
}

Paraphrased flow only. Match method names to your installed v5 client. Pass the taskID each response returns.

Cleanup after move has started

If move already started, do not delete the temp index in a finally block. Algolia may still reference it during the swap. Only remove a temp index when you abort before move (copy or batch failed mid-flight). I learned that after a "cleanup" step raced a move that was still finishing.

When a rebuild fails, I now check Algolia task status before rerunning. Half the time the previous run completed and my script merely ran out of patience. For nightly jobs I log the temp index name and the last task id so the morning shift can tell "still indexing" from "actually broken."

Large catalogs are not a reason to fork Algolia's whole helper forever. They are a reason to know where the fifty-poll ceiling lives and when your wrapper should stop delegating to it.


Happy coding! Sander