apify/crawlee · error · TypeError

Cannot set property '${String(prop)}' on serviceLocator dire

Error message

Cannot set property '${String(prop)}' on serviceLocator directly. Use the setter methods (e.g. setConfiguration(), setStorageBackend()) instead.

What it means

The serviceLocator export is wrapped in a Proxy whose set trap throws a TypeError for any direct property assignment. Service registration is intentionally write-protected; it must go through the typed setter methods (setConfiguration(), setStorageBackend(), setEventManager(), setLogger()).

Source

Thrown at packages/core/src/service_locator.ts:401

            serviceLocatorStorage.enterWith(serviceLocator);
        },
        exitScope: () => {
            serviceLocatorStorage.enterWith(previousStore as any); // casting to any so that `undefined` is accepted - this "unsets" the AsyncLocalStorage
        },
    };
}

export const serviceLocator = new Proxy({} as ServiceLocatorInterface, {
    get(_target, prop) {
        const active = serviceLocatorStorage.getStore() ?? globalServiceLocator;
        const value = Reflect.get(active, prop, active);
        if (typeof value === 'function') {
            return value.bind(active);
        }
        return value;
    },
    set(_target, prop) {
        throw new TypeError(
            `Cannot set property '${String(prop)}' on serviceLocator directly. Use the setter methods (e.g. setConfiguration(), setStorageBackend()) instead.`,
        );
    },
});

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Replace direct assignment with the corresponding setter method (setConfiguration(), setEventManager(), setStorageBackend(), setLogger()).
  2. Read services via getConfiguration(), getLogger(), etc., or through the proxy's get trap, which returns bound methods.
  3. Never mutate the locator object; construct a new ServiceLocator for different wiring.
  4. In tests, use dependency injection via constructor options (serviceLocator) instead of patching the global locator.

Example fix

// before
serviceLocator.configuration = new Configuration();

// after
serviceLocator.setConfiguration(new Configuration());
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Object.isFrozen(Object.getPrototypeOf(serviceLocator))) { /* ok to read */ }
// always register via:
// typeof serviceLocator.setConfiguration === 'function'

Type guard

function canRegisterService(locator: unknown): locator is { setConfiguration: (c: Configuration) => void } {
    return typeof locator === 'object' && locator !== null &&
        typeof (locator as any).setConfiguration === 'function';
}

Try / catch

try {
    (serviceLocator as any).anything = 1;
} catch (e) {
    if (e instanceof TypeError && /serviceLocator directly/.test(e.message)) {
        serviceLocator.setConfiguration(cfg); // use setters
    } else throw e;
}

Prevention

When it happens

Trigger: Any code doing serviceLocator.someService = x, serviceLocator['#logger'] = y, Object.assign(serviceLocator, {...}), or a transpiled/test framework writing onto the locator object.

Common situations: Trying to monkey-patch the locator in tests, naive dependency injection via assignment, or code migrated from an older version where the locator was a plain object.

Related errors


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