apify/crawlee · error · Error
Cannot extend LaunchContext with key
Error message
Cannot extend LaunchContext with key: ${key}, because it's reserved. What it means
LaunchContext.extend() lets users attach arbitrary fields to a launch context, but some field names are internally reserved. If you pass a field whose name collides with a reserved internal field (e.g. launchOptions, useIncognitoPages, proxyUrl), extend() refuses to override it so internal behavior cannot be broken. The offending key name is included in the message.
Solutions
- Remove the reserved key from the object you pass to extend() and set it through the intended API (LaunchContext/PuppeteerPlugin/PlaywrightPlugin constructor options or launchOptions) instead.
- Filter the object before extending, e.g. omit keys like launchOptions, userDataDir, proxyUrl, useIncognitoPages.
- Check the reserved field list in packages/browser-pool/src/launch-context.ts to know which names are off-limits.
Example fix
// before
launchContext.extend({ proxyUrl: 'http://proxy:8080', myData: 1 });
// after
const { proxyUrl, ...custom } = options; // proxyUrl is reserved
launchContext.extend(custom);
// configure proxy via the plugin/launch context options instead Defensive patterns
Strategy: validation
Validate before calling
const RESERVED = ['launchOptions', 'userDataDir', 'proxyUrl', 'useIncognitoPages', 'experimentalContainers']; const safe = Object.fromEntries(Object.entries(fields).filter(([k]) => !RESERVED.includes(k))); launchContext.extend(safe);
Type guard
function hasReservedKeys(obj: Record<string, unknown>, reserved: string[]): boolean {
return Object.keys(obj).some((k) => reserved.includes(k));
} Try / catch
try {
launchContext.extend(fields);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Cannot extend LaunchContext')) {
console.warn(`Dropping reserved key: ${err.message.match(/key: ([^,]+)/)?.[1]}`);
} else throw err;
} Prevention
- Keep custom data under a namespace key (e.g. extend({ custom: {...} })) to avoid collisions.
- Never spread raw launch/plugin options objects into extend().
- Review the #reservedFieldNames list in launch-context.ts when adding keys.
When it happens
Trigger: Calling launchContext.extend({ key: value }) where key is one of LaunchContext's #reservedFieldNames (its own internal configuration properties, such as launchOptions, userDataDir, proxyUrl, useIncognitoPages, experimentalContainers).
Common situations: Passing a whole options object (e.g. spreading launch options or plugin config) into extend() without filtering out reserved keys; renaming/refactors that introduce a key that collides with an internal field; attempting to override proxy or headless settings via extend instead of the proper constructor/launch options.
Related errors
- Page with ID: already exists.
- A new page can be created with provided context only when…
- A new page can be created with provided context only when…
- At most one of `body`, `form` and `json` may be specified…
- BrowserController already has a browser instance assigned.
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/d6f3d37e69a26270.
Report an issue: GitHub.
Appendix: source
Thrown at packages/browser-pool/src/launch-context.ts:136
this.#proxyUrl = proxyUrl;
// Computed here (not in a field initializer) so that all fields already exist; the accessors live on
// the prototype, so they are never own keys and have to be listed explicitly.
this.#reservedFieldNames = [...Reflect.ownKeys(this), 'proxyUrl', 'remoteToken', 'extend'];
}
/**
* Extend the launch context with any extra fields.
* This is useful to keep state information relevant
* to the browser being launched. It ensures that
* no internal fields are overridden and should be
* used instead of property assignment.
*/
extend<T extends Record<PropertyKey, unknown>>(fields: T): void {
Object.entries(fields).forEach(([key, value]) => {
if (this.#reservedFieldNames.includes(key)) {
throw new Error(`Cannot extend LaunchContext with key: ${key}, because it's reserved.`);
} else {
Reflect.set(this, key, value);
}
});
}
/**
* Sets a proxy URL for the browser.
* Use `undefined` to unset existing proxy URL.
*/
set proxyUrl(url: string | undefined) {
if (!url) {
this.#proxyUrl = undefined;
return;
}
const urlInstance = new URL(url);
urlInstance.pathname = '/';View on GitHub (pinned to dbe57fb09c)