{"record":{"id":"10b5c2031112f912","repo":"agalwood/Motrix","slug":"plugin-http-response-too-large","errorCode":"plugin.http.response_too_large","errorMessage":"Response body exceeded ${maxBodyBytes} bytes","messagePattern":"Response body exceeded (.+?) bytes","errorType":"exception","errorClass":"HttpError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/capabilities/http.ts","lineNumber":514,"sourceCode":"  try {\n    for await (const chunk of response.body) {\n      const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n      totalBytes += buf.byteLength\n      if (totalBytes > maxBodyBytes) {\n        capped = true\n        internalCtrl.abort('body_too_large')\n        response.body.destroy?.()\n        break\n      }\n      chunks.push(buf)\n    }\n  } catch {\n    // Ignore stream errors that arise from aborting the body read.\n  }\n  doCleanup()\n\n  if (capped) {\n    throw new HttpError(\n      'plugin.http.response_too_large',\n      `Response body exceeded ${maxBodyBytes} bytes`\n    )\n  }\n\n  const rawBody = Buffer.concat(chunks)\n  let parsedBody: unknown\n  if (responseType === 'bytes') {\n    parsedBody = new Uint8Array(\n      rawBody.buffer,\n      rawBody.byteOffset,\n      rawBody.byteLength\n    )\n  } else if (responseType === 'json') {\n    parsedBody = JSON.parse(rawBody.toString('utf8'))\n  } else {\n    parsedBody = rawBody.toString('utf8')\n  }","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/capabilities/http.ts#L496-L532","documentation":"While streaming the response body, accumulated bytes exceeded maxBodyBytes (default 50 MB, hard ceiling 200 MB). The internal controller is aborted with reason 'body_too_large', the body stream is destroyed, and after read-loop cleanup the capped flag triggers this error. This prevents a single response from exhausting plugin memory.","triggerScenarios":"Downloading a large file; an API that returns an unbounded result set; a server streaming a log/dump; a malicious or misconfigured endpoint returning megabytes of data; opts.maxBodyBytes set lower than the legitimate payload.","commonSituations":"Default 50 MB too small for legitimate media downloads; plugin does not pre-check Content-Length; paginated API that ignores limit params.","solutions":["If the payload is legitimate, raise opts.maxBodyBytes up to the 200 MB hard ceiling.","Stream large artifacts to disk via fs rather than loading into memory via http.","Inspect Content-Length before reading the body and bail early.","Narrow the request (pagination, field selection, range headers) to reduce payload size."],"exampleFix":"// before\nawait http.request({ url: bigFileUrl, responseType: 'bytes' })\n\n// after\nawait http.request({ url: bigFileUrl, responseType: 'bytes', maxBodyBytes: 200 * 1024 * 1024 })","handlingStrategy":"validation","validationCode":"// pre-flight via HEAD to learn size, before paying for the body\nconst head = await http.request({ url, method: 'HEAD', responseType: 'text' })\nconst len = Number(head.headers['content-length'] ?? 0)\nif (len && len > maxAllowed) throw new Error('too large; refusing to download')","typeGuard":null,"tryCatchPattern":"try {\n  return await http.request({ ...opts, maxBodyBytes: 200 * 1024 * 1024 })\n} catch (e) {\n  if (e instanceof HttpError && e.code === 'plugin.http.response_too_large') {\n    // switch to streaming-to-disk path instead of buffering\n  } else throw e\n}","preventionTips":["Set opts.maxBodyBytes explicitly based on what your plugin can legitimately consume.","HEAD-check Content-Length before downloading unknown artifacts.","Stream large payloads to fs rather than buffering in memory via http."],"tags":["http","memory","dos","safety","streaming"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}