apify/crawlee · error · ServiceConflictError
ServiceConflictError('EventManager', eventManager, this.#eve
Error message
ServiceConflictError('EventManager', eventManager, this.#eventManager) What it means
setEventManager() throws ServiceConflictError if a different EventManager instance is registered when one already exists in the ServiceLocator. This guards the singleton invariant: all components must share one EventManager so events route consistently.
Source
Thrown at packages/core/src/service_locator.ts:229
this.getLogger().warning(
'Implicit creation of event manager will implicitly set configuration as side effect. ' +
'It is advised to explicitly first set the configuration instead.',
);
}
this.#eventManager = LocalEventManager.fromConfiguration(this.getConfiguration());
}
return this.#eventManager;
}
setEventManager(eventManager: EventManager): void {
// Same instance, no need to do anything
if (this.#eventManager === eventManager) {
return;
}
// Already have a different event manager that was retrieved
if (this.#eventManager) {
throw new ServiceConflictError('EventManager', eventManager, this.#eventManager);
}
this.#eventManager = eventManager;
}
getStorageBackend(): StorageBackend {
if (!this.#storageBackend) {
this.getLogger().debug(
'No storage backend set, implicitly creating and using the default storage backend ' +
'(FileSystemStorageBackend when persistStorage is enabled, MemoryStorageBackend otherwise).',
);
if (!this.#configuration) {
this.getLogger().warning(
'Implicit creation of storage backend will implicitly set configuration as side effect. ' +
'It is advised to explicitly first set the configuration instead.',
);
}
const configuration = this.getConfiguration();View on GitHub (pinned to dbe57fb09c)
Solutions
- Register your custom EventManager before anything else initializes the default one.
- Reuse the existing event manager (getEventManager()) instead of constructing a new one.
- Only call setEventManager() once per locator lifetime.
- Reset/recreate the ServiceLocator between test cases that each set their own event manager.
Example fix
// before
const crawler = new CheerioCrawler(...); // initializes default EventManager
locator.setEventManager(myEventManager); // throws
// after
const locator = new ServiceLocator();
locator.setEventManager(myEventManager); // before any retrieval
const crawler = new CheerioCrawler({ ..., serviceLocator: locator }); Defensive patterns
Strategy: validation
Validate before calling
let existing;
try { existing = locator.getEventManager(); } catch { existing = null; }
if (existing && existing !== myEventManager) { /* skip registration */ } Try / catch
try {
locator.setEventManager(em);
} catch (e) {
if (e instanceof ServiceConflictError) em = locator.getEventManager();
else throw e;
} Prevention
- Inject custom EventManager only during initial wiring, before any crawler/storage access.
- Subscribe to the existing manager's events instead of replacing it.
- Keep locator setup in a single shared bootstrap module.
When it happens
Trigger: Calling setEventManager(emA), then setEventManager(emB) with a different EventManager instance; or retrieving the event manager first and later trying to inject a custom one.
Common situations: Manually wiring a custom EventManager after crawlee already initialized one (e.g. by creating a crawler or accessing events), or double initialization in tests/HMR.
Related errors
- Cannot set() a borrowed OwnedOrInjected value
- ServiceConflictError('Configuration', configuration, this.#c
- ServiceConflictError('StorageBackend', storageBackend, this.
- 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/0ab15c7debc6113a.
Report an issue: GitHub.