awslabs/llrt · error · AssertionError

expected function to throw an error, but it didn't

Error message

expected function to throw an error, but it didn't

What it means

The synchronous path of toThrow throws this AssertionError when the supplied function executes without throwing any error, and no specific expected error was matched (expected not a string/regex/error). It is the sync counterpart of the promise variants in [7]/[8].

Solutions

  1. Make the function throw for the invalid case: add `throw new Error('...')` where appropriate.
  2. Pass a function/thunk to expect, not the invoked result: expect(() => fn()).toThrow().
  3. Verify the inputs actually trigger the throwing branch (e.g. truly invalid arguments).
  4. Update the test if non-throwing behavior is now correct, using toBe/toEqual instead.

Example fix

// before
function parse(n) { return Number(n); } // never throws
expect(() => parse('x')).toThrow();
// after
function parse(n) { const v = Number(n); if (Number.isNaN(v)) throw new Error('invalid number'); return v; }
expect(() => parse('x')).toThrow();
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the function throws before asserting
let threw = false; try { fn(); } catch { threw = true; } if (!threw) throw new Error('fn must throw for these inputs');

Try / catch

try { expect(() => fn()).toThrow(ExpectedError); } catch (e) { /* fn did not throw — fix fn or the inputs */ }

Prevention

When it happens

Trigger: expect(() => fn()).toThrow() where fn() completes normally; also expect(fn).toThrow() with a function reference that doesn't throw when invoked.

Common situations: Bug was fixed so the function no longer throws; guard clauses return early instead of throwing; input that should be invalid is silently coerced; test passed an already-invoked result instead of a thunk (e.g. expect(fn()).toThrow() with a resolved value).

Related errors


AI-assisted analysis of awslabs/llrt@742fc00b82 (2026-09-12). Data as JSON: /api/errors/cb6c79569ce29233. Report an issue: GitHub.

Appendix: source

Thrown at llrt_core/src/modules/js/@llrt/expect/jest-expect.ts:467

          return;
        }
      } else {
        let isThrow = false;
        try {
          obj();
        } catch (err) {
          isThrow = true;
          thrown = err;
        }

        if (!isThrow && !isNot) {
          const message =
            utils.flag(this, "message") ||
            "expected function to throw an error, but it didn't";
          const error = {
            showDiff: false,
          };
          throw new AssertionError(message, error, utils.flag(this, "ssfi"));
        }
      }

      if (typeof expected === "function") {
        // @ts-ignore
        const name = expected.name || expected.prototype.constructor.name;
        return this.assert(
          thrown && thrown instanceof expected,
          `expected error to be instance of ${name}`,
          `expected error not to be instance of ${name}`,
          expected,
          thrown
        );
      }

      if (expected instanceof Error) {
        return this.assert(
          thrown && expected.message === thrown.message,

View on GitHub (pinned to 742fc00b82)