denoland/deno · error · ERR_INVALID_ARG_TYPE

ERR_INVALID_ARG_TYPE

ERR_INVALID_ARG_TYPE

Error message

The "ext" argument must be of type string. Received ${received}

What it means

`path.posix.basename(path, ext)` requires the second argument to be a string or undefined. The polyfill throws ERR_INVALID_ARG_TYPE for any other type. `null` throws too, because the default parameter only replaces undefined.

Source

Thrown at ext/node/polyfills/path/_posix.ts:283

    } else {
      // We saw the first non-path separator
      matchedSlash = false;
    }
  }

  if (end === -1) return hasRoot ? "/" : ".";
  if (hasRoot && end === 1) return "//";
  return StringPrototypeSlice(path, 0, end);
}

/**
 * Return the last portion of a `path`. Trailing directory separators are ignored.
 * @param path to process
 * @param ext of path directory
 */
function basename(path: string, ext = ""): string {
  if (ext !== undefined && typeof ext !== "string") {
    throw new ERR_INVALID_ARG_TYPE("ext", ["string"], ext);
  }
  assertPath(path);

  let start = 0;
  let end = -1;
  let matchedSlash = true;
  let i: number;

  if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
    if (ext.length === path.length && ext === path) return "";
    let extIdx = ext.length - 1;
    let firstNonSlashEnd = -1;
    for (i = path.length - 1; i >= 0; --i) {
      const code = StringPrototypeCharCodeAt(path, i);
      if (code === CHAR_FORWARD_SLASH) {
        // If we reached a path separator that was not part of a set of path
        // separators at the end of the string, stop now
        if (!matchedSlash) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Omit the ext argument when you do not need it.
  2. Null-safe the lookup: `file.match(/\.[^.]+$/)?.[0] ?? ''`.
  3. Coerce explicitly when coercion is intended: `String(ext)`.
  4. Default destructure the parameter: `function base(p, ext = '')`.

Example fix

// before
const ext = file.match(/\.[^.]+$/); // null when no extension
path.basename(file, ext);

// after
const ext = file.match(/\.[^.]+$/)?.[0] ?? '';
path.basename(file, ext);
Defensive patterns

Strategy: type-guard

Validate before calling

const safeExt = (ext) => (ext == null ? '' : String(ext));
path.basename(file, safeExt(ext));

Type guard

function isOptionalString(v: unknown): v is string | undefined {
  return v === undefined || typeof v === 'string';
}

Prevention

When it happens

Trigger: `path.basename(file, null)` from a regex match or indexOf-style lookup that returned null; `path.basename(p, 5)`; passing an options object in place of ext; `path.basename(p, ext)` where ext came from destructured config.

Common situations: Extension computed with `String.prototype.match()` that returns null when there is no match. Optional config fields with no default. Porting shell habits where the extension filter is a separate flag.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/d2829f6b83765f9f. Report an issue: GitHub.