apify/crawlee · error · Error
At least one of "sources" or "sourcesFunction" must be provi
Error message
At least one of "sources" or "sourcesFunction" must be provided.
What it means
A RequestList must be given its URLs either statically via the sources array or dynamically via sourcesFunction. Constructing one with neither would produce an always-empty list, so the constructor validates and throws immediately.
Source
Thrown at packages/core/src/storages/request_list.ts:333
/**
* To create new instance of `RequestList` we need to use `RequestList.open()` factory method.
* @param options All `RequestList` configuration options
* @internal
*/
private constructor(options: RequestListOptions = {}) {
const {
sources,
sourcesFunction,
persistStateKey,
persistRequestsKey,
state,
proxyConfiguration,
keepDuplicateUrls,
httpClient,
} = parseArgument(options, requestListOptionsSchema);
if (!(sources || sourcesFunction)) {
throw new Error('At least one of "sources" or "sourcesFunction" must be provided.');
}
this.#persistStateKey = persistStateKey ? `CRAWLEE_${persistStateKey}` : persistStateKey;
this.#persistRequestsKey = persistRequestsKey ? `CRAWLEE_${persistRequestsKey}` : persistRequestsKey;
this.#initialState = state;
this.#httpClient = httpClient;
// If this option is set then all requests will get a pre-generated unique ID and duplicate URLs will be kept in the list.
this.#keepDuplicateUrls = keepDuplicateUrls;
// Will be empty after initialization to save memory.
this.#sources = sources ? [...sources] : [];
this.#sourcesFunction = sourcesFunction;
// The proxy configuration used for `requestsFromUrl` requests.
this.#proxyConfiguration = proxyConfiguration;
this.persistState = this.persistState.bind(this);View on GitHub (pinned to dbe57fb09c)
Solutions
- Pass sources: new RequestList({ sources: ['https://example.com'] })
- Or pass sourcesFunction: () => fetchUrls() to generate sources at initialize() time
- If requests are added manually later, use RequestQueue instead of RequestList
Example fix
// before
const list = await RequestList.open('list', {});
// after
const list = await RequestList.open('list', { sources: ['https://example.com'] }); Defensive patterns
Strategy: validation
Validate before calling
function assertRequestListOptions(options) {
if (!options.sources && !options.sourcesFunction) {
throw new TypeError('RequestList needs "sources" or "sourcesFunction"');
}
}
assertRequestListOptions({ sources: config.urls }); Type guard
const hasSources = (o) => Array.isArray(o?.sources) && o.sources.length > 0 || typeof o?.sourcesFunction === 'function';
Prevention
- Never omit both sources and sourcesFunction
- Validate config-provided sources arrays are non-empty before constructing
- Use RequestQueue if you only add requests dynamically at runtime
When it happens
Trigger: new RequestList({ sources: undefined, sourcesFunction: undefined }) — e.g. both options omitted, misspelled (source/sources), or computed as undefined by a ternary/config lookup.
Common situations: Loading source config from env/JSON that came back empty and was assigned verbatim; renaming options during a refactor; copying an example and deleting both fields while intending to addRequest() later.
Understand the failure class
Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.
Related errors
- Failed to infer format from the path: '${path}'. Supported f
- Unsupported format: '${format}'. Use one of ${supportedForma
- Invalid "proxyUrl". Unsupported protocol: ${proxyUrl}.
- `alwaysEnqueue` cannot be used together with a custom `uniqu
- Cannot persist state. options.persistStateKey is not set.
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/6d3a8687c9a174a8.
Report an issue: GitHub.