denoland/deno · error · TypeError

ERR_INVALID_ARG_VALUE

ERR_INVALID_ARG_VALUE

Error message

The argument 'error' may not be an empty object. Received ${expected}

What it means

In assert.throws/assert.rejects, when the expected argument is a plain object it acts as a property validator: its own keys are checked against the thrown error, so an object with zero keys validates nothing and is rejected up front (ext/node/polyfills/assert.ts:246) with ERR_INVALID_ARG_VALUE 'error may not be an empty object'. Note that Error instances are exempt (name/message are implicitly added), but {}, Object.create(null), or objects whose own enumerable keys are absent throw.

Source

Thrown at ext/node/polyfills/assert.ts:246

      const err = new AssertionError({
        actual,
        expected,
        message,
        operator: "deepStrictEqual",
        stackStartFn: fn,
        diff: this?.[kOptions]?.diff,
      });
      err.operator = fn.name;
      throw err;
    } else {
      // Handle validation objects.
      const keys = ObjectKeys(expected);
      // Special handle errors to make sure the name and the message are
      // compared as well.
      if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, expected)) {
        ArrayPrototypePush(keys, "name", "message");
      } else if (keys.length === 0) {
        throw new ERR_INVALID_ARG_VALUE(
          "error",
          expected,
          "may not be an empty object",
        );
      }
      for (const key of new SafeArrayIterator(keys)) {
        if (
          typeof actual[key] === "string" &&
          isRegExp(expected[key]) &&
          RegExpPrototypeExec(expected[key], actual[key]) !== null
        ) {
          continue;
        }
        compareExceptionKey(actual, expected, key, message, keys, fn);
      }
      return;
    }
    // Guard instanceof against arrow functions as they don't have a prototype.

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Drop the second argument entirely to assert 'throws with anything': assert.throws(fn)
  2. Specify at least one property to validate: assert.throws(fn, { name: 'TypeError' })
  3. When building expected dynamically, fall back to a bare call when the matcher object would be empty: Object.keys(matcher).length ? assert.throws(fn, matcher) : assert.throws(fn)

Example fix

// before
assert.throws(() => JSON.parse('{'), {});

// after
assert.throws(() => JSON.parse('{'), { name: 'SyntaxError' });
Defensive patterns

Strategy: validation

Validate before calling

if (matcher && typeof matcher === 'object' && !Object.keys(matcher).length) matcher = undefined; // assert 'throws with anything'

Type guard

function isNonEmptyErrorMatcher(v) { return v == null || typeof v === 'function' || Object.keys(v).length > 0 || v instanceof Error; }

Try / catch

try { assert.throws(fn, matcher); } catch (e) { if (e.code === 'ERR_INVALID_ARG_VALUE' && /empty object/.test(e.message)) assert.throws(fn); else throw e; }

Prevention

When it happens

Trigger: assert.throws(fn, {}); assert.rejects(promise, {}); dynamic expected objects built from optional fields where every field was undefined or non-enumerable, leaving zero keys.

Common situations: Placeholder tests written as assert.throws(fn, {}) meaning 'any throw'; config-driven assertion builders merging optional matchers that end up empty; objects whose properties are defined only on the prototype (ObjectKeys sees none).

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/f761b1aadf1a76f0. Report an issue: GitHub.