apify/crawlee · error · ServiceConflictError
ServiceConflictError('Logger', logger, this.#logger)
Error message
ServiceConflictError('Logger', logger, this.#logger) What it means
setLogger() throws ServiceConflictError if a different CrawleeLogger is registered after one already exists in the ServiceLocator. The library assumes a single logger instance so all components emit logs through the same sink.
Source
Thrown at packages/core/src/service_locator.ts:287
}
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) {
throw new ServiceConflictError('Logger', logger, this.#logger);
}
this.#logger = logger;
}
getChildLog(prefix: string): CrawleeLogger {
return this.getLogger().child({ prefix });
}
getStorageInstanceManager(): StorageInstanceManager {
if (!ServiceLocator.#storageInstanceManager) {
ServiceLocator.#storageInstanceManager = new StorageInstanceManager();
}
return ServiceLocator.#storageInstanceManager;
}
reset(): void {
this.#configuration = undefined;View on GitHub (pinned to dbe57fb09c)
Solutions
- Inject the custom logger before any crawlee component initializes (before crawler construction or storage access).
- Reuse getLogger() and wrap/extend it rather than replacing it.
- Call setLogger() only once per ServiceLocator lifetime.
- Prefer configuring log level via Log.getLogLevel()/log options rather than swapping logger instances.
Example fix
// before
const crawler = new PlaywrightCrawler(...); // default logger registered
locator.setLogger(myLogger); // throws
// after
const locator = new ServiceLocator();
locator.setLogger(myLogger);
const crawler = new PlaywrightCrawler({ ..., serviceLocator: locator }); Defensive patterns
Strategy: validation
Validate before calling
let existing;
try { existing = locator.getLogger(); } catch { existing = null; }
if (existing && existing !== myLogger) { /* logger already registered */ } Try / catch
try {
locator.setLogger(logger);
} catch (e) {
if (e instanceof ServiceConflictError) {
const base = locator.getLogger();
logger = Object.assign(Object.create(Object.getPrototypeOf(base)), logger);
} else throw e;
} Prevention
- Register the logger in the same bootstrap step as other services, before crawlers run.
- Extend the existing logger (child logger) rather than replacing it.
- Adjust log levels via configuration instead of swapping instances.
When it happens
Trigger: Calling setLogger(loggerA) then setLogger(loggerB); or using the default logger implicitly and later trying to inject a custom logger via the locator.
Common situations: Installing a custom log implementation after crawlee initialization, or in tests where multiple setups each register a logger on the shared locator.
Related errors
- Cannot set() a borrowed OwnedOrInjected value
- ServiceConflictError('Configuration', configuration, this.#c
- ServiceConflictError('EventManager', eventManager, this.#eve
- ServiceConflictError('StorageBackend', storageBackend, this.
- 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/4f2628e01d846c09.
Report an issue: GitHub.