Sửa lỗi Node.js: RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stdout maxBuffer length exceeded

intermediate💚 Node.js2026-06-06| Node.js (tất cả phiên bản), Linux, macOS, Windows

Error Message

RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stdout maxBuffer length exceeded
#nodejs#child_process#exec#spawn#gỡ lỗi

Lỗi

Khi chạy một lệnh shell thông qua child_process.exec(), ứng dụng của bạn có thể đột ngột dừng lại với lỗi cụ thể này:

RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stdout maxBuffer length exceeded
    at Socket.ondata (node:internal/child_process:871:22)
    at Socket.emit (node:events:518:28)
    at addChunk (node:internal/streams/readable:559:12)
    at readableAddChunk (node:internal/streams/readable:532:9)
    at Socket.Readable.push (node:internal/streams/readable:373:10)
    at Pipe.onStreamRead (node:internal/stream_base_commons:190:23) {
  code: 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'
}

Tại sao lỗi này xảy ra?

Node.js sử dụng child_process.exec() để chạy một lệnh và đợi lệnh đó hoàn thành. Nó cố gắng dồn toàn bộ đầu ra (stdout và stderr) vào một vùng đệm bộ nhớ (buffer) duy nhất trước khi trả lại cho hàm callback của bạn. Theo mặc định, Node.js giới hạn buffer này ở mức 1 Megabyte (1.048.576 bytes). Nếu bạn đang sử dụng một phiên bản Node.js cũ (trước v12), giới hạn đó chỉ vỏn vẹn 200 Kilobytes.

Khi lệnh của bạn—có thể là mysqldump, một lệnh git log dài dằng dặc, hoặc find trên một thư mục khổng lồ—tạo ra 1.1 MB văn bản, Node.js sẽ "hoảng loạn". Nó dừng tiến trình ngay lập tức để ngăn ứng dụng chiếm dụng hết RAM và làm sập toàn bộ máy chủ.

Giải pháp 1: Nâng giới hạn (Cách sửa nhanh)

Nếu bạn biết chính xác lượng dữ liệu sẽ trả về và nó không quá lớn (ví dụ: 5MB thay vì 1MB), bạn có thể tăng giới hạn này theo cách thủ công bằng tùy chọn maxBuffer. Đây là cách nhanh nhất để script của bạn hoạt động trở lại.

Giá trị này được tính bằng byte. Để cho phép đầu ra lên đến 10MB, hãy sử dụng 1024 * 1024 * 10.

const { exec } = require('child_process');

// Tăng buffer lên 10MB
const options = {
    maxBuffer: 1024 * 1024 * 10 
};

exec('cat logs/huge-access.log', options, (error, stdout, stderr) => {
    if (error) {
        console.error(`Tiến trình thất bại: ${error.message}`);
        return;
    }
    console.log(`Đã đọc ${stdout.length} ký tự đầu ra.`);
});

Mẹo: Chỉ sử dụng cách này cho các đầu ra có kích thước trung bình và có thể dự đoán được. Nếu kích thước đầu ra thay đổi thất thường, bạn chỉ đang trì hoãn việc bị crash mà thôi.

Giải pháp 2: Sử dụng child_process.spawn (Cách sửa chuyên nghiệp)

Đối với các tệp lớn hoặc dữ liệu không thể dự đoán, exec() không phải là công cụ phù hợp. Thay vào đó, hãy chuyển sang child_process.spawn().

Hãy coi exec giống như việc tải xuống toàn bộ bộ phim trước khi xem, trong khi spawn giống như việc xem phim trực tuyến (streaming). spawn cung cấp stdoutstderr dưới dạng các Streams. Bạn xử lý dữ liệu theo từng khối nhỏ (thường là 64KB) ngay khi chúng tới, giúp giữ mức sử dụng bộ nhớ thấp bất kể kích thước tệp là bao nhiêu.

const { spawn } = require('child_process');

// Stream dữ liệu thay vì lưu vào buffer
const child = spawn('cat', ['logs/huge-access.log']);

child.stdout.on('data', (chunk) => {
    console.log(`Đang xử lý khối dữ liệu ${chunk.length} bytes...`);
    // Phân tích dữ liệu tại đây hoặc truyền (pipe) sang tệp khác
});

child.stderr.on('data', (data) => {
    console.error(`Lỗi đầu ra: ${data}`);
});

child.on('close', (code) => {
    console.log(`Tiến trình kết thúc với mã thoát ${code}`);
});

Khi nào nên sử dụng: Luôn sử dụng spawn cho các tác vụ chạy lâu, xuất cơ sở dữ liệu hoặc khi truyền dữ liệu trực tiếp đến một đích đến khác như S3 bucket hoặc phản hồi HTTP.

Cách xác minh kết quả

- **Kiểm tra kích thước thực tế:** Chạy lệnh trong terminal và truyền vào `wc -c` (ví dụ: `ls -R / | wc -c`) để xem chính xác số byte bạn đang xử lý.
- **Áp dụng cách sửa đã chọn:** Hoặc tăng `maxBuffer` cao hơn một chút so với con số đó, hoặc viết lại logic bằng `spawn`.
- **Kiểm tra tải (Stress test):** Chạy script với đầu vào lớn nhất có thể. Nếu nó hoàn thành mà không gặp lỗi `RangeError`, bạn đã thành công.

Lưu ý kỹ thuật cuối cùng

- **Chi phí UTF-16:** Trong Node.js, các chuỗi sử dụng mã hóa UTF-16. Một buffer đầu ra 10MB thực tế có thể chiếm tới 20MB RAM vật lý. Việc đặt `maxBuffer` thành 1GB là con đường dẫn đến lỗi `Out of Memory` (OOM).
- **Mã thoát (Exit Codes):** Hãy nhớ rằng ngay cả khi buffer không bị tràn, tiến trình vẫn có thể thất bại vì những lý do khác. Luôn kiểm tra đối tượng `error` trong `exec` hoặc sự kiện `close` trong `spawn`.
- **Promises:** Nếu bạn thích dùng `async/await`, hãy sử dụng `util.promisify(exec)`. Bạn có thể truyền `maxBuffer` làm đối số thứ hai: `await execPromise('cmd', { maxBuffer: 1024 * 5000 })`.

Related Error Notes