apify/crawlee · error · TypeError
Configuration is immutable. Pass options via the constructor
Error message
Configuration is immutable. Pass options via the constructor instead.
What it means
Crawlee Configuration instances are frozen: resolved option fields get setters that throw a TypeError. Options must be provided when the Configuration is constructed; mutating a global or existing instance afterwards is deliberately unsupported.
Source
Thrown at packages/core/src/configuration.ts:267
const parsed = fieldDef.schema.safeParse(undefined);
values[key] = parsed.success ? parsed.data : undefined;
}
return values;
}
/**
* Registers getters (and throwing setters) on the instance for each field.
*/
private registerAccessors(): void {
const fields = (this.constructor as typeof Configuration).fields;
const descriptors: PropertyDescriptorMap = {};
for (const key of Object.keys(fields)) {
descriptors[key] = {
get: () => this.#resolvedValues[key],
set() {
throw new TypeError('Configuration is immutable. Pass options via the constructor instead.');
},
enumerable: true,
configurable: false,
};
}
Object.defineProperties(this, descriptors);
}
/**
* Reads the first defined env var value for a field definition.
* Empty strings are treated as unset, falling through to crawlee.json or schema defaults.
* (Crawlee v3 coerced `''` to `false`/`0`/`''` per type — v4 drops that for consistency.)
*/
private static readEnvVar(fieldDef: ConfigField): string | undefined {
if (!fieldDef.envVar) return undefined;
const envVars = Array.isArray(fieldDef.envVar) ? fieldDef.envVar : [fieldDef.envVar];
for (const envVar of envVars) {View on GitHub (pinned to dbe57fb09c)
Solutions
- Pass all options to the constructor: `new Configuration({ persistStorage: false, ... })`.
- Replace the global instance early with `Configuration.getGlobalConfig()` / set a fresh `new Configuration(...)` before any storage clients initialize.
- Restructure code so options are known before the run starts instead of mutating later.
Example fix
// before
const config = Configuration.getGlobal();
config.persistStorage = false;
// after
const config = new Configuration({ persistStorage: false }); Defensive patterns
Strategy: validation
Validate before calling
// configure once, at startup, before anything reads the config
const config = new Configuration({ persistStorage: false, defaultDatasetId: 'my-dataset' }); Type guard
function isMutableConfigWrite(key: string): boolean { return ['persistStorage','defaultDatasetId','memoryMbytes'].includes(key); } // treat all resolved fields as read-only instead Try / catch
try { (config as any).persistStorage = false; } catch (err) { if (err instanceof TypeError && err.message.includes('immutable')) { config = new Configuration({ persistStorage: false }); } else { throw err; } } Prevention
- Never mutate Configuration instances; construct a new one with all options
- Set global configuration at process start before storage clients initialize
- Update code written against older, mutable Crawlee Configuration APIs
When it happens
Trigger: Assigning to a configuration field at runtime, e.g. `Configuration.getGlobal().persistStorage = false` or `config.defaultDatasetId = 'x'`, after the instance was created.
Common situations: Toggling settings mid-run (persistStorage, proxies) in tests; mutating `Configuration.getGlobal()` imported from another module; code written against older Crawlee versions where the config was mutable.
Related errors
- availableMemoryRatio is not set in configuration.
- Duplicate load signal name ${JSON.stringify(name)}: ${hint}
- The `minConcurrency`/`maxConcurrency`/`initialConcurrency`/`
- The `requestManager` option cannot be used in conjunction wi
- Cannot decide what to purge before running again: `sameDomai
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/4b504552ed2cba66.
Report an issue: GitHub.