apify/crawlee · error · ServiceConflictError
ServiceConflictError('StorageBackend', storageBackend, this.
Error message
ServiceConflictError('StorageBackend', storageBackend, this.#storageBackend) What it means
setStorageBackend() throws ServiceConflictError when a different StorageBackend is provided after one has already been set or retrieved. The locator enforces one storage backend per lifetime so all storages (datasets, KV stores, request queues) resolve consistently.
Source
Thrown at packages/core/src/service_locator.ts:268
localDataDirectory: configuration.storageDir,
logger: this.getLogger().child({ prefix: 'FileSystemStorageBackend' }),
})
: new MemoryStorageBackend({
logger: this.getLogger().child({ prefix: 'MemoryStorageBackend' }),
});
}
return this.#storageBackend;
}
setStorageBackend(storageBackend: StorageBackend): void {
// Same instance, no need to do anything
if (this.#storageBackend === storageBackend) {
return;
}
// Already have a different storage backend that was retrieved
if (this.#storageBackend) {
throw new ServiceConflictError('StorageBackend', storageBackend, this.#storageBackend);
}
this.#storageBackend = storageBackend;
}
getLogger(): CrawleeLogger {
if (!this.#logger) {
this.#logger = new ApifyLogAdapter(log);
}
return this.#logger;
}
setLogger(logger: CrawleeLogger): void {
if (this.#logger === logger) {
return;
}
if (this.#logger) {View on GitHub (pinned to dbe57fb09c)
Solutions
- Call setStorageBackend() once, before any storage access or crawler creation.
- Use the same StorageBackend instance throughout the process.
- If switching backends is required, create a new ServiceLocator and use it when constructing crawlers.
- Set the backend via the standard configuration (e.g. CRAWLEE_STORAGE_BACKEND env or Configuration) instead of manual injection after init.
Example fix
// before
await Dataset.open(); // default backend initialized
locator.setStorageBackend(new MemoryStorage()); // throws
// after
const locator = new ServiceLocator();
locator.setStorageBackend(new MemoryStorage());
await Dataset.open('my-dataset', { serviceLocator: locator }); Defensive patterns
Strategy: validation
Validate before calling
let existing;
try { existing = locator.getStorageBackend(); } catch { existing = null; }
if (existing && existing !== myBackend) { /* backend already chosen */ } Try / catch
try {
locator.setStorageBackend(backend);
} catch (e) {
if (e instanceof ServiceConflictError) {
backend = locator.getStorageBackend();
} else throw e;
} Prevention
- Choose the storage backend (MemoryStorage vs default) once at startup via configuration.
- Never open storages before registering a custom backend.
- Avoid mixing init paths (env config + manual injection).
When it happens
Trigger: Calling setStorageBackend(backendA) then setStorageBackend(backendB); or letting crawlee initialize the default backend and afterwards injecting a custom one (e.g. MemoryStorage or a cloud backend).
Common situations: Mixing MemoryStorage and default filesystem/cloud storage initialization, or switching storage backends mid-run after a dataset/KV store was already accessed.
Related errors
- Cannot set() a borrowed OwnedOrInjected value
- ServiceConflictError('Configuration', configuration, this.#c
- ServiceConflictError('EventManager', eventManager, this.#eve
- ServiceConflictError('Logger', logger, this.#logger)
- This crawler instance is already running, you can add more r
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/0bc0729e55985fc7.
Report an issue: GitHub.