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

  1. Register your custom EventManager before anything else initializes the default one.
  2. Reuse the existing event manager (getEventManager()) instead of constructing a new one.
  3. Only call setEventManager() once per locator lifetime.
  4. 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

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


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/0ab15c7debc6113a. Report an issue: GitHub.