apify/crawlee · error · Error

Request options are not valid, the 'url' property is not a s

Error message

Request options are not valid, the 'url' property is not a string. Input: ${inspect(opts)}

What it means

RequestQueue.addRequestsBatched() streams inputs through generateRequests(), validating each item. If an object provides a url property that is defined but not a string (number, object, null, array), the library rejects it because Request construction requires a string URL.

Source

Thrown at packages/core/src/storages/request_queue.ts:598

     * @param options Options for the request queue
     */
    async addRequestsBatched(
        requests: ReadonlyDeep<RequestsLike>,
        options: AddRequestsBatchedOptions = {},
    ): Promise<AddRequestsBatchedResult> {
        parseArgument(requests, iterableSchema);

        const { forefront, waitForAllRequestsToBeAdded, batchSize, waitBetweenBatchesMillis, maxNewRequests } =
            parseArgument(options, addRequestsBatchedOptionsSchema);

        const addRequest = this.addRequest.bind(this);

        async function* generateRequests() {
            for await (const opts of requests) {
                // Validate the input
                if (typeof opts === 'object' && opts !== null) {
                    if (opts.url !== undefined && typeof opts.url !== 'string') {
                        throw new Error(
                            `Request options are not valid, the 'url' property is not a string. Input: ${inspect(opts)}`,
                        );
                    }

                    if (opts.id !== undefined) {
                        throw new Error(
                            `Request options are not valid, the 'id' property must not be present. Input: ${inspect(opts)}`,
                        );
                    }

                    if (
                        (opts as any).requestsFromUrl !== undefined &&
                        typeof (opts as any).requestsFromUrl !== 'string'
                    ) {
                        throw new Error(
                            `Request options are not valid, the 'requestsFromUrl' property is not a string. Input: ${inspect(opts)}`,
                        );
                    }

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Sanitize inputs: keep only items where typeof url === 'string' and non-empty
  2. Coerce valid values (e.g. String(row.url)) and drop invalid rows before adding
  3. Fix upstream data extraction producing non-string urls
  4. Add strict typing/DTO validation (zod etc.) on incoming request sources

Example fix

// before
await requestQueue.addRequestsBatched(rows.map(r => ({ url: r.url }))); // r.url may be null
// after
const valid = rows.filter(r => typeof r.url === 'string' && r.url.length > 0)
    .map(r => ({ url: r.url }));
await requestQueue.addRequestsBatched(valid);
Defensive patterns

Strategy: type-guard

Validate before calling

const valid = requests.filter(r => !(r && typeof r === 'object' && 'url' in r) || typeof r.url === 'string');

Type guard

function hasStringUrl(r) {
  return r === null || typeof r !== 'object' || r.url === undefined || typeof r.url === 'string';
}

Try / catch

try {
  await queue.addRequestsBatched(requests);
} catch (err) {
  if (err.message.includes("'url' property is not a string")) {
    logger.error(`Invalid request options: ${err.message}`);
  } else throw err;
}

Prevention

When it happens

Trigger: Passing an async iterable/array to addRequestsBatched where an item has url: null, url: 123, url: {...}; data from parsed JSON/CSV where url is missing-but-keyed or numeric; TypeScript types bypassed.

Common situations: Importing scraped data where the url column is empty/NaN; mapping API responses with inconsistent field types; template objects with url accidentally set to a nested object.

Related errors


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