This error occurs when you pass an argument of the wrong type to a Node.js function that expects a string. It's commonly triggered when file system methods receive a number, undefined, null, or object instead of a string path.
The ERR_INVALID_ARG_TYPE error is a TypeError that Node.js throws when a function receives an argument of an unexpected type. This is a strict type validation error that helps catch programmer mistakes early. In the case of the 'path' argument, many Node.js APIs (fs.readFile, fs.writeFile, path.join, etc.) require a string, Buffer, or URL object for file paths. When you accidentally pass a number, boolean, undefined, null, or plain object, Node.js immediately throws this error to prevent silent failures or undefined behavior. This error is usually a result of incorrect variable types in your code—either from accessing undefined properties, type conversions gone wrong, or misunderstanding what a function expects. The error message clearly indicates both what type was expected and what type was actually received, making it straightforward to debug.
Check the stack trace to identify which function call is failing. The error message will show the file and line number:
TypeError: The 'path' argument must be of type string. Received type number
at validatePath (node:internal/fs/utils:168:13)
at Object.readFile (node:fs/promises:32:18)
at /path/to/your/file.js:15:22Look at line 15 in your file to see what's being passed to readFile.
Add console.log or use a debugger to check what type the variable actually is:
import { readFile } from 'fs/promises';
let filePath = getUserPath(); // Could be undefined or wrong type
console.log('filePath type:', typeof filePath);
console.log('filePath value:', filePath);
// This might fail if filePath is not a string
const data = await readFile(filePath, 'utf-8');Use typeof or console.log to inspect the actual value before it reaches the function.
Make sure your path variable is defined and contains a string before use:
// ❌ BAD - filePath might be undefined
const filePath = config.path;
await readFile(filePath);
// ✓ GOOD - Provide a default or check for undefined
const filePath = config.path ?? './default.txt';
if (typeof filePath !== 'string') {
throw new Error('Path must be a string');
}
await readFile(filePath);Use optional chaining and nullish coalescing operators to handle missing values safely.
If you intended to use a string but received a different type, convert it explicitly:
// ❌ BAD - Passing a number accidentally
const fileIndex = 5;
const data = await readFile(fileIndex); // Fails: number is not a string
// ✓ GOOD - Convert to string explicitly
const filePath = String(fileIndex); // Converts 5 to '5'
const data = await readFile(filePath);
// ✓ GOOD - Or use string interpolation
const filePath = `file_${fileIndex}.txt`;
const data = await readFile(filePath);
// ✓ GOOD - For arrays, use proper path joining
import { join } from 'path';
const dirName = 'data';
const fileName = 'file.txt';
const filePath = join(dirName, fileName); // Returns 'data/file.txt'
const data = await readFile(filePath);Use explicit type conversion or the path.join utility instead of string concatenation.
When destructuring objects, check that properties exist before using them:
// ❌ BAD - Destructuring could fail silently
const { filePath } = config;
await readFile(filePath); // filePath is undefined if config lacks this property
// ✓ GOOD - Provide defaults in destructuring
const { filePath = './default.txt' } = config;
if (typeof filePath !== 'string') {
throw new Error('filePath must be a string');
}
await readFile(filePath);
// ✓ GOOD - Explicit validation
const { filePath } = config;
if (!filePath || typeof filePath !== 'string') {
throw new Error('Invalid filePath: must be a non-empty string');
}
await readFile(filePath);Always validate destructured values before using them in functions with strict type requirements.
If using TypeScript, define proper types for your variables:
import { readFile } from 'fs/promises';
interface Config {
filePath: string; // Explicitly require string
}
async function loadData(config: Config): Promise<string> {
// TypeScript will error if filePath is not a string
const data = await readFile(config.filePath, 'utf-8');
return data;
}
// ✓ This compiles fine
await loadData({ filePath: './data.txt' });
// ❌ TypeScript error: Type 'number' is not assignable to type 'string'
await loadData({ filePath: 123 });TypeScript's type system prevents these errors before runtime.
Understanding Argument Type Validation in Node.js
Node.js strictly validates argument types for built-in functions to prevent silent failures. Unlike JavaScript where type coercion often occurs automatically, Node.js APIs are strict about types. For example:
// JavaScript allows loose type coercion
console.log('5' + 3); // '53' - concatenation
// But Node.js fs methods don't allow this coercion
const fs = require('fs');
fs.readFileSync(123); // ❌ Throws ERR_INVALID_ARG_TYPE, doesn't coerce
fs.readFileSync('123'); // ✓ Works - expects string pathThis strictness helps catch bugs early and makes behavior predictable.
Validation with Zod or Runtime Type Checking
For applications handling user input or configuration, use schema validation libraries:
import { z } from 'zod';
import { readFile } from 'fs/promises';
const ConfigSchema = z.object({
filePath: z.string().min(1, 'filePath must not be empty'),
});
async function loadConfig(raw: unknown) {
try {
const config = ConfigSchema.parse(raw);
const data = await readFile(config.filePath, 'utf-8');
return data;
} catch (err) {
if (err instanceof z.ZodError) {
console.error('Invalid config:', err.errors);
} else {
throw err;
}
}
}Zod validates input at runtime and provides detailed error messages.
Common Functions with Type Restrictions
These Node.js functions strictly validate the 'path' argument:
- fs.readFile(path, ...) - expects string, Buffer, or URL
- fs.writeFile(path, data, ...) - expects string, Buffer, or URL
- fs.access(path, ...) - expects string, Buffer, or URL
- path.join(...paths) - expects all arguments to be strings
- path.resolve(...paths) - expects all arguments to be strings
- fs.stat(path, ...) - expects string, Buffer, or URL
Debugging Type Issues
Use Object.prototype.toString.call() for accurate type detection:
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
console.log(getType('hello')); // 'String'
console.log(getType(123)); // 'Number'
console.log(getType(undefined)); // 'Undefined'
console.log(getType(null)); // 'Null'
console.log(getType({})); // 'Object'
console.log(getType([])); // 'Array'This is more reliable than typeof for complex types.
Buffer and URL as Valid Path Types
Some Node.js functions accept Buffer or URL in addition to strings:
import { readFile } from 'fs/promises';
import { fileURLToPath } from 'url';
// ✓ String path
await readFile('./file.txt');
// ✓ Buffer path
await readFile(Buffer.from('./file.txt'));
// ✓ URL object
const fileUrl = new URL('file:///home/user/file.txt');
await readFile(fileUrl);
// ✓ File URL to path conversion
const filePath = fileURLToPath(import.meta.url);
await readFile(filePath);Check function documentation to see which types are accepted for each API.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams