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

  1. Call setStorageBackend() once, before any storage access or crawler creation.
  2. Use the same StorageBackend instance throughout the process.
  3. If switching backends is required, create a new ServiceLocator and use it when constructing crawlers.
  4. 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

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


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