{"record":{"id":"4f27398368494206","repo":"discordjs/discord.js","slug":"unable-to-resolve-body","errorCode":null,"errorMessage":"Unable to resolve body.","messagePattern":"Unable to resolve body\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/rest/src/strategies/undiciRequest.ts","lineNumber":84,"sourceCode":"\t} else if (body instanceof UndiciFormData) {\n\t\treturn body;\n\t} else if (body instanceof FormData) {\n\t\treturn globalToUndiciFormData(body);\n\t} else if ((body as Iterable<Uint8Array>)[Symbol.iterator]) {\n\t\tconst chunks = [...(body as Iterable<Uint8Array>)];\n\n\t\treturn Buffer.concat(chunks);\n\t} else if ((body as AsyncIterable<Uint8Array>)[Symbol.asyncIterator]) {\n\t\tconst chunks: Uint8Array[] = [];\n\n\t\tfor await (const chunk of body as AsyncIterable<Uint8Array>) {\n\t\t\tchunks.push(chunk);\n\t\t}\n\n\t\treturn Buffer.concat(chunks);\n\t}\n\n\tthrow new TypeError(`Unable to resolve body.`);\n}\n\nfunction globalToUndiciFormData(fd: globalThis.FormData): UndiciFormData {\n\tconst clone = new UndiciFormData();\n\n\tfor (const [name, value] of fd.entries()) {\n\t\tif (typeof value === 'string') {\n\t\t\tclone.append(name, value);\n\t\t} else {\n\t\t\tclone.append(name, value, value.name);\n\t\t}\n\t}\n\n\treturn clone;\n}\n","sourceCodeStart":66,"sourceCodeEnd":100,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/rest/src/strategies/undiciRequest.ts#L66-L100","documentation":"resolveBody in the undiciRequest strategy throws this TypeError when it cannot turn the provided request body into something sendable: the body is neither a Buffer/Uint8Array, nor a string, nor a supported stream/FormData-like object after all checks. It is a client-side input problem, thrown before any HTTP traffic leaves the process.","triggerScenarios":"Passing an unsupported body type to a REST request's files/body options — e.g. `files: [{ attachment: 123 }]` with a number, an object where a Buffer/string/stream was required, a Blob/File from an incompatible realm, or a globalThis.FormData that fails conversion — as when uploading attachments via rest.post(Routes.channelMessages(id), { files: [...] }).","commonSituations":"Downloading an image then forgetting buffer conversion (passing a plain object); passing null/undefined plus an options shape mismatch after a library version change (undiciRequest strategy replaced node-fetch internals); reading file as a path string instead of a Buffer in a Node strategy; mixing browser File objects into a Node process with different undici globals.","solutions":["Convert the body to a Buffer/string before sending: fs.readFileSync(path) or await readFile(path) for files, JSON.stringify() for raw bodies.","For file uploads, pass files: [{ attachment: buffer, name: 'file.png' }] with a Buffer or stream, never a path string or bare object.","If passing FormData, use the library's re-exported undici FormData or node's global FormData consistently; don't mix realms.","Check the DiscordjsError/wrapper: this is a TypeError — log typeof/value of what you passed into body/files to spot the wrong type.","Ensure RESTOptions.makeRequest/strategy and undici versions are matched to your @discordjs/rest version (stale lockfile can cause realm mismatches)."],"exampleFix":"// before\nawait rest.post(Routes.channelMessages(id), {\n  files: [{ attachment: 'avatar.png', name: 'avatar.png' }], // path string -> TypeError\n});\n// after\nconst buffer = await fs.promises.readFile('avatar.png');\nawait rest.post(Routes.channelMessages(id), {\n  files: [{ attachment: buffer, name: 'avatar.png' }],\n});","handlingStrategy":"validation","validationCode":"function assertSendableBody(body: unknown): asserts body is Buffer | string {\n  if (Buffer.isBuffer(body) || typeof body === 'string' || body instanceof Uint8Array) return;\n  throw new TypeError(`Body must be Buffer/string/Uint8Array, got ${typeof body}`);\n}\n// for files:\nfiles.forEach(f => {\n  if (!(f.attachment instanceof Buffer || typeof f.attachment === 'string' && fs.existsSync(f.attachment) === false)) {\n    throw new TypeError('attachment must be a Buffer or readable stream');\n  }\n});","typeGuard":"function isSendableBody(b: unknown): b is Buffer | string | Uint8Array {\n  return Buffer.isBuffer(b) || b instanceof Uint8Array || typeof b === 'string';\n}","tryCatchPattern":"try {\n  await rest.post(route, { files });\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('Unable to resolve body')) {\n    console.error('Bad request body type passed to files/body:', inspectBody(files));\n  } else throw e;\n}","preventionTips":["Always read files into Buffers (fs.promises.readFile) before attaching.","Never pass file path strings as attachment; pass Buffer or stream plus name.","Log typeof and constructor of the body when building requests during development.","Use the library's exported FormData rather than mixing realms.","Pin @discordjs/rest and undici versions together and dedupe in the lockfile."],"tags":["typeerror","request-body","file-upload","undici","client-bug"],"backgroundTag":"invalid-request-body-type","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}