How to fix Error: EAGAIN: resource temporarily unavailable in Node.js
The EAGAIN error indicates that a requested I/O operation cannot be completed immediately because the resource is temporarily unavailable. This is a recoverable error that signals Node.js to retry the operation later rather than failing permanently.
Quick fix
Fixes EAGAIN
# Check maximum open file descriptors
ulimit -n
# Check maximum user processes
ulimit -u
# View all limits
ulimit -a- Check current system resource limits
- Implement retry logic for EAGAIN errors
- Increase system resource limits
- Fix file descriptor and process leaks
# Check maximum open file descriptorsulimit -n# Check maximum user processesulimit -u# View all limitsulimit -aError: EAGAIN: resource temporarily unavailable
What this error means
The EAGAIN (Error AGAIn) error is a POSIX system-level error code that occurs when attempting to perform I/O operations on resources that are temporarily unavailable. In Node.js, this error commonly appears during file operations, reading from stdin, spawning child processes, or working with non-blocking I/O operations. Unlike permanent errors, EAGAIN indicates a transient condition - the operation failed not because of incorrect parameters or permissions, but because the system temporarily lacks the resources to complete it. This typically occurs when working with non-blocking file descriptors where data isn't immediately available for reading, or when system resource limits (like open file descriptors or process counts) are temporarily exhausted. The error is particularly common in high-throughput applications that perform many concurrent I/O operations, spawn numerous child processes, or work with streams and file descriptors extensively. Understanding that EAGAIN is temporary and recoverable is crucial for implementing proper error handling strategies.
Typical symptoms
- Operations on fs.readFileSync(0) or process.stdin fail intermittently
- child_process.spawn() or fork() operations fail with EAGAIN
- File read/write operations throw EAGAIN during high load
- Applications enter high CPU usage loops when spawning many processes
- Error appears randomly during large test suites execution
- Serial port or stdin reading operations fail unexpectedly
Common causes
- System has reached the maximum number of open file descriptors (ulimit -n)
- Process count limit has been exceeded (ulimit -u)
- Reading from stdin or file descriptor when no data is currently available
- Spawning too many child processes simultaneously without proper cleanup
- Non-blocking file descriptor operations attempted before data is ready
- System resource exhaustion from rogue processes or cron jobs
- File descriptor leaks from improperly closed streams or handles
How to fix "Error: EAGAIN: resource temporarily unavailable"
Verify your current ulimit settings to identify if you're hitting system constraints:
# Check maximum open file descriptors
ulimit -n
# Check maximum user processes
ulimit -u
# View all limits
ulimit -aOn Linux, you can also check current file descriptor usage:
# Count open file descriptors for current process
ls /proc/$$/fd | wc -l
# For a specific Node.js process
ls /proc/<PID>/fd | wc -lHandle EAGAIN errors gracefully by retrying the operation after a short delay:
const fs = require('fs');
function readWithRetry(fd, buffer, offset, length, position, maxRetries = 3) {
let retries = 0;
while (retries < maxRetries) {
try {
return fs.readSync(fd, buffer, offset, length, position);
} catch (error) {
if (error.code === 'EAGAIN' && retries < maxRetries - 1) {
retries++;
// Brief delay before retry
const start = Date.now();
while (Date.now() - start < 10) {} // 10ms blocking wait
continue;
}
throw error;
}
}
}For async operations, use a more elegant retry approach:
async function readWithRetryAsync(fd, buffer, offset, length, position, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fs.promises.read(fd, buffer, offset, length, position);
} catch (error) {
if (error.code === 'EAGAIN' && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 10));
continue;
}
throw error;
}
}
}Temporarily increase limits for your current session:
# Increase file descriptor limit
ulimit -n 4096
# Increase process limit
ulimit -u 2048For permanent changes on Linux, edit /etc/security/limits.conf:
* soft nofile 4096
* hard nofile 8192
* soft nproc 2048
* hard nproc 4096For systemd services, add to your service file:
[Service]
LimitNOFILE=4096
LimitNPROC=2048On macOS, edit /Library/LaunchDaemons/limit.maxfiles.plist or use launchctl.
Ensure all file descriptors and child processes are properly closed:
const fs = require('fs');
const { spawn } = require('child_process');
// Always close file descriptors
const fd = fs.openSync('file.txt', 'r');
try {
// perform operations
} finally {
fs.closeSync(fd);
}
// Use graceful-fs for automatic retry handling
const gracefulFs = require('graceful-fs');
gracefulFs.readFile('file.txt', 'utf8', (err, data) => {
// graceful-fs automatically retries on EAGAIN
});
// Properly manage child processes
const child = spawn('command', ['arg1', 'arg2']);
child.on('exit', (code) => {
console.log(`Process exited with code ${code}`);
});
child.on('error', (error) => {
if (error.code === 'EAGAIN') {
console.error('Resource temporarily unavailable, retry later');
}
});
// Limit concurrent child processes
const MAX_CONCURRENT = 10;
let activeProcesses = 0;
const queue = [];
function spawnWithLimit(command, args) {
if (activeProcesses < MAX_CONCURRENT) {
activeProcesses++;
const child = spawn(command, args);
child.on('exit', () => {
activeProcesses--;
if (queue.length > 0) {
const next = queue.shift();
spawnWithLimit(next.command, next.args);
}
});
return child;
} else {
queue.push({ command, args });
}
}Install and use graceful-fs, which automatically handles EAGAIN and EMFILE errors:
npm install graceful-fsReplace native fs with graceful-fs:
// Instead of:
// const fs = require('fs');
// Use:
const fs = require('graceful-fs');
// All fs operations now automatically retry on EAGAIN
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error after retries:', err);
return;
}
console.log(data);
});Or patch fs globally at application startup:
const gracefulFs = require('graceful-fs');
gracefulFs.gracefulify(require('fs'));Advanced notes
Understanding Non-Blocking I/O: EAGAIN is primarily associated with non-blocking file descriptors. When you read from a non-blocking fd and no data is available, the system returns EAGAIN instead of blocking the thread. Node.js internally handles this for streams and async operations, but sync operations or direct file descriptor access can expose this error.
Process Spawning Limits: When spawning child processes, EAGAIN can occur if the system hits the per-user process limit. On Linux, this is typically controlled by RLIMIT_NPROC. Applications that spawn many short-lived processes (like build tools or test runners) should implement process pooling to avoid this limit.
Stdin Reading Edge Cases: Reading from stdin using fs.readSync(0) can produce EAGAIN if stdin is non-blocking and no input is available. This is especially common when using stdin in TTY mode or after other libraries have modified stdin's flags. Consider using readline or process.stdin.read() which handle this automatically.
Container Environments: In Docker containers or Kubernetes pods, default ulimits may be lower than the host system. Always configure appropriate limits in your container specifications:
# Kubernetes example
spec:
containers:
- name: app
resources:
limits:
cpu: "1"
memory: "512Mi"
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: ["SYS_RESOURCE"]File Descriptor Monitoring: For production applications, monitor file descriptor usage using process metrics. Many APM tools can track this, or use custom monitoring:
setInterval(() => {
const used = process._getActiveHandles().length + process._getActiveRequests().length;
console.log(`Active handles: ${used}`);
}, 60000);Kernel Parameters: On Linux, system-wide limits are set in /proc/sys/fs/file-max (total file descriptors) and /proc/sys/kernel/pid_max (total processes). These kernel parameters may need adjustment on high-load servers.
Frequently asked questions
- What does "EAGAIN" mean?
- The EAGAIN (Error AGAIn) error is a POSIX system-level error code that occurs when attempting to perform I/O operations on resources that are temporarily unavailable. js, this error commonly appears during file operations, reading from stdin, spawning child processes, or working with non-blocking I/O operations. Unlike permanent errors, EAGAIN indicates a transient condition - the operation failed not because of incorrect parameters or permissions, but because the system temporarily lacks the resources to complete it.
- How do I fix "EAGAIN"?
- The EAGAIN error indicates that a requested I/O operation cannot be completed immediately because the resource is temporarily unavailable. This is a recoverable error that signals Node.js to retry the operation later rather than failing permanently.
- What causes "EAGAIN"?
- System has reached the maximum number of open file descriptors (ulimit -n) Process count limit has been exceeded (ulimit -u) Reading from stdin or file descriptor when no data is currently available
- Is the "EAGAIN" error serious?
- It is a medium-severity error โ more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- ๐Node.js Official Documentation
- ๐Node.js File System Module - Error Handling(Official Docs)
- ๐
- ๐
- ๐