{"record":{"id":"47f9e6ed9ea82313","repo":"apify/crawlee","slug":"at-most-one-of-body-form-and-json-may-be-sp","errorCode":null,"errorMessage":"At most one of `body`, `form` and `json` may be specified in sendRequest arguments","messagePattern":"At most one of `body`, `form` and `json` may be specified in sendRequest arguments","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/http-crawler/src/internals/utils.ts","lineNumber":29,"sourceCode":"\n/**\n * Converts {@apilink HttpRequestOptions} to a {@apilink HttpRequest}.\n */\nexport function processHttpRequestOptions({\n    searchParams,\n    form,\n    json,\n    username,\n    password,\n    ...request\n}: HttpRequestOptions): HttpRequest {\n    const url = new URL(request.url);\n    const headers = new Headers(request.headers);\n\n    applySearchParams(url, searchParams);\n\n    if ([request.body, form, json].filter((value) => value !== undefined).length > 1) {\n        throw new Error('At most one of `body`, `form` and `json` may be specified in sendRequest arguments');\n    }\n\n    const body = (() => {\n        if (form !== undefined) {\n            return Readable.from(new URLSearchParams(form).toString());\n        }\n\n        if (json !== undefined) {\n            return Readable.from(JSON.stringify(json));\n        }\n\n        if (request.body !== undefined) {\n            return Readable.from(request.body);\n        }\n\n        return undefined;\n    })();\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/http-crawler/src/internals/utils.ts#L11-L47","documentation":"processHttpRequestOptions() converts sendRequest/HttpCrawler request options into a HttpRequest. Only one request payload encoding is allowed: raw `body`, `form`, or `json`. When more than one of these options is defined (not `undefined`), the function aborts because it cannot decide which serialization to use (body stream vs urlencoded vs JSON.stringify) and which content-type to set.","triggerScenarios":"Calling sendRequest / HttpCrawler / crawler.run with e.g. both `json` and `body` set, both `form` and `json` set, or a request object that already carries `body` while the caller also passes `json` or `form`. Values of `null` count as defined — only `undefined` is treated as absent.","commonSituations":"Migrating code that sets a default JSON payload and then adds a raw body override; building request options dynamically and forgetting to clear a previously assigned `form`; spreading merged option objects where two payload keys survive; assuming `null` means 'not set'.","solutions":["Keep exactly one payload option: delete or set to `undefined` the others before calling.","If you need a custom payload, put it in `body` (as string/Buffer/stream) and set the Content-Type header yourself instead of combining with `form`/`json`.","Check merged/spread option objects so a previous `json`/`form` is not retained alongside `body`.","Use `null`-safe checks in your own code — note the library only skips `undefined`, so pass `undefined`, not `null`, for absent options."],"exampleFix":"// before\nawait crawler.sendRequest({ url, json: { a: 1 }, body: 'raw-data' });\n\n// after\nawait crawler.sendRequest({ url, json: { a: 1 } });\n// or for raw payloads:\nawait crawler.sendRequest({ url, body: 'raw-data', headers: { 'content-type': 'text/plain' } });","handlingStrategy":"validation","validationCode":"const payloadKeys = ['body', 'form', 'json'].filter((k) => opts[k] !== undefined);\nif (payloadKeys.length > 1) {\n    throw new Error(`Only one of body/form/json allowed, got: ${payloadKeys.join(', ')}`);\n}","typeGuard":"function hasSinglePayload(o: { body?: unknown; form?: unknown; json?: unknown }): boolean {\n    return ['body', 'form', 'json'].filter((k) => o[k as keyof typeof o] !== undefined).length <= 1;\n}","tryCatchPattern":"try {\n    await crawler.sendRequest(opts);\n} catch (err) {\n    if ((err as Error).message.includes('At most one of `body`')) {\n        logger.error('Conflicting payload options', { opts });\n    }\n    throw err;\n}","preventionTips":["Build request options through a single helper that enforces one payload key.","Always use `undefined` (never `null`) to mark absent options.","When merging/spreading option objects, explicitly delete stale body/form/json keys.","Write a unit test per payload combination used in your project."],"tags":["http","request-options","validation","api-misuse"],"backgroundTag":"conflicting-request-body-options","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}