denoland/deno · error · TypeError

ERR_AMBIGUOUS_ARGUMENT

ERR_AMBIGUOUS_ARGUMENT

Error message

The "error/message" argument is ambiguous. The error message "${actual.message}" is identical to the message.

What it means

When a string error spec is used with assert.throws/assert.rejects, expectsError compares it to the thrown error's message. If the two are identical, the call is ambiguous — the same text is both the expected message and (implicitly) the assertion's failure message — so the polyfill throws ERR_AMBIGUOUS_ARGUMENT instead of guessing. Older Node versions only warned (DEP0018-adjacent behavior); current Node and this polyfill throw.

Solutions

  1. Wrap the expectation in a RegExp: assert.throws(fn, /boom/).
  2. Or use a validation object: assert.throws(fn, { message: 'boom' }).
  3. If the string was meant as the assertion's failure message, move it to the third argument: assert.throws(fn, undefined, 'boom') — or simply assert.throws(fn) and let the thrown error speak for itself.
  4. When building expectations dynamically, derive them from a different source than the runtime message (a shared constant is fine, but change one side's form, e.g. new RegExp(escaped)).

Example fix

// before
assert.throws(() => { throw new Error('boom'); }, 'boom');

// after
assert.throws(() => { throw new Error('boom'); }, /boom/);
Defensive patterns

Strategy: validation

Validate before calling

// Compare by pattern, never by identical literal string
const expectMessage = (msg) => (msg instanceof RegExp ? msg : new RegExp(msg));
assert.throws(fn, expectMessage(thrownMessage));

Type guard

const isUnambiguousErrorSpec = (error, actualMsg) =>
  !(typeof error === 'string' && error === actualMsg);

Try / catch

try {
  assert.throws(fn, 'boom');
} catch (e) {
  if (e.code === 'ERR_AMBIGUOUS_ARGUMENT') {
    assert.throws(fn, /boom/); // re-express the expectation unambiguously
  } else throw e;
}

Prevention

When it happens

Trigger: assert.throws(() => { throw new Error('boom') }, 'boom') — the thrown Error's .message is strictly equal to the string passed as the expectation.

Common situations: Tests written as assert.throws(fn, err.message) after capturing a message dynamically, making expected and actual text identical by construction; generated tests that build the expected string from the same constant the code under test throws; upgrading Node/Deno from versions that tolerated this with a deprecation warning, breaking previously green suites.

Related errors


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

Appendix: source

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

function expectsError(
  stackStartFn,
  actual,
  error,
  message,
) {
  if (typeof error === "string") {
    if (arguments.length === 4) {
      throw new ERR_INVALID_ARG_TYPE("error", [
        "Object",
        "Error",
        "Function",
        "RegExp",
      ], error);
    }
    if (typeof actual === "object" && actual !== null) {
      if (actual.message === error) {
        throw new ERR_AMBIGUOUS_ARGUMENT(
          "error/message",
          `The error message "${actual.message}" is identical to the message.`,
        );
      }
    } else if (actual === error) {
      throw new ERR_AMBIGUOUS_ARGUMENT(
        "error/message",
        `The error "${actual}" is identical to the message.`,
      );
    }
    message = error;
    error = undefined;
  } else if (
    error != null &&
    typeof error !== "object" &&
    typeof error !== "function"
  ) {
    throw new ERR_INVALID_ARG_TYPE("error", [

View on GitHub (pinned to 9ad36f7a2c)