denoland/deno · error · NodeTypeError
ERR_INVALID_ARG_TYPE
ERR_INVALID_ARG_TYPE
Error message
The "path" argument must be of type string or an instance of Buffer. Received ${actual} What it means
The internal join helper that appends a directory-entry name to a base path accepts exactly two combinations: both arguments strings, or both Uint8Array/Buffer. A mixed pair (string + Buffer) or any other type matches neither branch and the helper throws ERR_INVALID_ARG_TYPE for the path argument. It backs readdir's dirent assembly, so type inconsistency between a base path and entry names is what surfaces it.
Source
Thrown at ext/node/polyfills/internal/fs/utils.mjs:281
lazyPath().default.join(path, lazyPath().default.sep),
);
// Ignore lint. `concat` is a 'node:buffer' static method on `Buffer`
// deno-lint-ignore deno-internal/prefer-primordials
return Buffer.concat([pathBuffer, name]);
}
if (typeof path === "string" && typeof name === "string") {
// deno-lint-ignore deno-internal/prefer-primordials -- `join` is a `node:path` function
return lazyPath().default.join(path, name);
}
if (isUint8Array(path) && isUint8Array(name)) {
// Ignore lint. `concat` is a 'node:buffer' static method on `Buffer`
// deno-lint-ignore deno-internal/prefer-primordials
return Buffer.concat([path, bufferSep, name]);
}
throw new ERR_INVALID_ARG_TYPE(
"path",
["string", "Buffer"],
path,
);
}
export function getDirents(path, { 0: names, 1: types }, callback) {
let i;
if (typeof callback === "function") {
const len = names.length;
let toFinish = 0;
callback = once(callback);
for (i = 0; i < len; i++) {
const type = types[i];
if (type === UV_DIRENT_UNKNOWN) {
const name = names[i];
const idx = i;
toFinish++;View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Make both operands the same type: path.toString('utf8') + name, or Buffer.concat([path, Buffer.from(name)]).
- Consume fs.Dirent directly (readdir with withFileTypes) and build paths with a single type discipline.
- Avoid Buffer paths unless NUL-byte-safe names are a hard requirement.
Example fix
// before (mixed types)
const full = join(bufPath, stringName); // Buffer + string -> throws
// after
const full = path.join(bufPath.toString('utf8'), stringName);
// or stay binary
const full = Buffer.concat([bufPath, Buffer.from('/'), Buffer.from(stringName)]); Defensive patterns
Strategy: type-guard
Validate before calling
const sameType = (a, b) =>
(typeof a === 'string' && typeof b === 'string') ||
(a instanceof Uint8Array && b instanceof Uint8Array);
if (!sameType(base, name)) {
throw new TypeError('path and name must both be strings or both Buffers');
} Type guard
function isHomogeneousPair(path, name) {
if (typeof path === 'string' && typeof name === 'string') return true;
if (path instanceof Uint8Array && name instanceof Uint8Array) return true;
return false;
} Prevention
- Pick one path representation (string or Buffer) per module and stick to it.
- Convert types at boundaries, never mid-pipeline.
- Prefer readdir with withFileTypes to get consistent name/path types.
When it happens
Trigger: Feeding readdir results back with mismatched types: a Buffer base path (encoding: 'buffer') combined with string names from another call, or the reverse; direct misuse of the internal join with heterogeneous arguments.
Common situations: Binary-safe path pipelines that convert only one side to Buffer; refactors that change one variable's type mid-pipeline; appending fs.Dirent names to a Buffer parent path.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ERR_INVALID_ARG_VALUE
- ERR_OUT_OF_RANGE
- the source path is not a valid file
- LAUFEY cache dir has no parent: {}
- LAUFEY backend binary has no parent directory: {}
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d58a6adfdf2d7e7c.
Report an issue: GitHub.